openapi: 3.1.0
info:
  title: Omni API
  description: |
    The Omni REST API provides programmatic access to your Omni instance for managing users, documents, queries, schedules, and more.  
  version: 1.0.0
  contact:
    name: Omni Support
    url: https://docs.omni.co

servers:
  - url: https://{instance}.omniapp.co/api
    description: Production
    variables:
      instance:
        default: blobsrus
        description: Your production Omni instance subdomain
  - url: https://{instance}.playground.exploreomni.dev/api
    description: Playground
    variables:
      instance:
        default: blobsrus
        description: Your playground Omni instance subdomain

security:
  - bearerAuth: []
  - orgApiKey: []

tags:
  - name: Who Am I
    description: Inspect your own user permissions
  - name: AI
    description: AI-powered query generation
  - name: AI Credit Controls
    description: Manage organization-level AI credit usage
  - name: AI Credit Usage
    description: Monitor AI credit usage
  - name: AI Evals
    description: Manage prompt sets and runs used to score AI quality against curated prompt suites.
  - name: AI Model Suggestions
    description: Manage AI-generated suggestions for shared models
  - name: AI Routines
    description: Manage Routines, which are scheduled, recurring AI-powered tasks that run automatically on your data
  - name: Apps
    description: Draft and publish app documents.
  - name: App downloads
    description: Download apps as PNG, PDF, XLSX, or CSV files
  - name: Branches
    description: List branches and open or close them to Viewers and Restricted Queriers
  - name: Documents v2
    description: |
      A draft-based workflow for creating and editing documents: create a document, patch a draft, then publish.
  - name: Documents
    description: Create, retrieve, and manage documents
  - name: API Tokens
    description: Manage API tokens (Organization keys, Personal Access Tokens, MCP OAuth grants)
  - name: IP Allowlist
    description: Manage the organization's IP allowlist
  - name: Connections
    description: Manage database connections
  - name: Connection environments
    description: Manage connection environments database connections
  - name: Color Palettes
    description: Manage custom chart color palettes
  - name: Content
    description: Unified content retrieval (documents and folders)
  - name: Content migration
    description: Export and import dashboards
  - name: Content validator
    description: Validate content against models and perform find/replace operations
  - name: Dashboard downloads
    description: Download dashboards and tiles as PDF, PNG, XLSX, CSV, or JSON files
  - name: Dashboard filters and controls
    description: Read and update dashboard filter and control default values
  - name: dbt
    description: Manage dbt configuration for connections
  - name: Document favorites
    description: Favorite and unfavorite documents
  - name: Document labels
    description: Apply and manage labels on documents
  - name: Document permissions
    description: Manage document-level access
  - name: Embed
    description: Manage embed sessions
  - name: Labels
    description: |
      Manage labels in the organization
  - name: Folders
    description: Create and organize content folders
  - name: Folder favorites
    description: Favorite and unfavorite folders
  - name: Folder labels
    description: Apply and manage labels on folders
  - name: Folder permissions
    description: Manage folder-level access
  - name: Jobs
    description: Check status of asynchronous jobs
  - name: Models
    description: Create and manage data models
  - name: Model branches
    description: Manage model branches and merge changes
  - name: Model git configuration
    description: Manage git configuration for shared models
  - name: Queries
    description: Execute workbook queries
  - name: Schedules
    description: Create and manage scheduled tasks
  - name: Schedule recipients
    description: Manage schedule recipients
  - name: Schema refresh schedules
    description: Manage automated schema refresh schedules for connections
  - name: Skills
    description: Manage skills, the saved slash-command instructions the Omni AI assistant runs
  - name: Topics
    description: Retrieve topic information from models
  - name: Uploads
    description: Manage file uploads
  - name: Users
    description: Manage users
  - name: User attributes
    description: Manage user attribute definitions
  - name: User groups
    description: Manage user groups
  - name: User model roles
    description: Manage model and connection role assignments for users
  - name: User group model roles
    description: Manage model and connection role assignments for user groups
  - name: Uploads
    description: Manage CSV and spreadsheet uploads

paths:
  /v1/whoami:
    get:
      description: |
        Retrieve the authenticated caller's own identity, API key scope, organization role, and resolved per-model permissions. Useful for allowing a caller to determine if they can execute a specific action without attempting it. Pass `modelId` to scope `rolesByModel` to specific models.

        This endpoint can be used by any user with a valid API key.
      operationId: whoami
      summary: Get current identity and permissions
      tags:
        - Who Am I
      parameters:
        - name: modelId
          in: query
          schema:
            type: string
            example: 550e8400-e29b-41d4-a716-446655440000
          required: false
          description: |
            Optional model filter. A single model ID or a comma-separated list.
            
            When provided, `rolesByModel` in the response will contain only these models. When omitted, models the caller can access are returned up to a limit; see `rolesByModelTruncated`.
      responses:
        '200':
          description: Caller's identity, key scope, org role, and per-model permissions
          content:
            application/json:
              schema:
                type: object
                required:
                  - keyScope
                  - orgRole
                  - rolesByModel
                  - user
                properties:
                  keyScope:
                    type: string
                    enum:
                      - user
                      - organization
                    description: |
                      Scope of the API key in use.
                      
                      - `user` - Personal Access Token. This is a user-scoped key (PAT/OAuth) that acts as a single user and cannot use SCIM, regardless of the user's organization role.
                      - `organization` - Organization API key
                  orgRole:
                    type: string
                    enum:
                      - MEMBER
                      - ORG_ADMIN
                    description: The caller's organization role.
                    example: MEMBER
                  rolesByModel:
                    type: object
                    additionalProperties:
                      $ref: '#/components/schemas/WhoamiModelRole'
                    description: Resolved role and effective permissions per model, keyed by model ID. Connection role resolves per shared model, so this is per-model rather than a single global role.
                  rolesByModelTruncated:
                    type: boolean
                    description: Present and `true` when `rolesByModel` was truncated because the caller can access more models than the unfiltered limit. Pass a `modelId` filter to retrieve specific models.
                  user:
                    $ref: '#/components/schemas/WhoamiUser'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '404':
          description: One or more requested `modelId`s do not exist or are not accessible to the caller
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'

  /v1/ai/branding:
    get:
      tags:
        - AI
      summary: Get AI Agent branding
      description: |
        Return the organization's AI Agent's [branding configuration](/ai/settings/branding), including display name, custom logo URL, and copy for AI Agent landing surfaces. Falls back to Omni's default values when custom branding hasn't been configured.
      security:
        - bearerAuth: []
      operationId: getAiBranding
      responses:
        '200':
          description: Successfully retrieved AI Agent branding
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    description: The display name for the AI Agent. Defaults to `Omni Agent` if branding hasn't been configured.
                    example: "Omni AI"
                  logoUrl:
                    type: string
                    nullable: true
                    description: Absolute URL to the custom logo image for the AI Agent. Returns `null` if no custom logo is configured.
                    example: "https://example.com/custom-logo.png"
                  headline:
                    type: string
                    description: Headline text displayed on AI Agent landing surfaces
                    example: "Welcome to Omni AI"
                  body:
                    type: string
                    description: Body copy displayed on AI Agent landing surfaces
                    example: "Ask questions about your data in natural language"
                  promptPlaceholder:
                    type: string
                    description: Placeholder text for the AI prompt input field
                    example: "Ask a question about your data..."
              examples:
                customBranding:
                  summary: Custom branding
                  value:
                    displayName: "DataBot"
                    logoUrl: "https://example.com/custom-logo.png"
                    headline: "Welcome to DataBot"
                    body: "Your intelligent data assistant"
                    promptPlaceholder: "What would you like to know?"
                defaultBranding:
                  summary: Default branding (fallback)
                  value:
                    displayName: "Omni AI"
                    logoUrl: null
                    headline: "Welcome to Omni AI"
                    body: "Ask questions about your data in natural language"
                    promptPlaceholder: "Ask a question about your data..."
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:
            - `Insufficient permissions` - User does not have AI permissions on any model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/generate-query:
    post:
      tags:
        - AI
      summary: Generate a query
      description: |
        Generate a structured Omni query from natural language using AI. 

        For exploratory or cross-topic questions, use [Create AI job](/api/ai/create-ai-job) instead.
      security:
        - bearerAuth: []
      operationId: generateQuery
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - modelId
              properties:
                modelId:
                  type: string
                  format: uuid
                  description: The ID of the model to run the query against
                currentTopicName:
                  type: string
                  description: Name of the base topic/table. If left empty, AI will automatically choose a topic based on the question.
                prompt:
                  type: string
                  default: ''
                  description: Natural language instruction describing the desired query output. If omitted, an empty prompt is sent.
                workbookUrl:
                  type: boolean
                  description: When `true`, returns an Omni workbook URL that opens and runs the generated query
                branchId:
                  type: string
                  description: The ID of the model branch to use to generate the query. If not provided, the `main` branch will be used. To retrieve branch IDs, use the [List models](/api/models/list-models) endpoint with `modelKind=BRANCH`.
                runQuery:
                  type: boolean
                  default: true
                  description: |
                    When `true` (default), the generated query is executed against the database and results are included in the response.

                    When `false`, only the generated query object is returned without executing it. Set to `false` to preview what query would be generated without incurring a database query.
                contextQuery:
                  allOf:
                    - $ref: '#/components/schemas/OmniQuery'
                  description: An optional query for the AI to start from, such as the base query for a follow-up prompt. Use this to reference previous queries or provide additional context for the generation.
                documentQueries:
                  type: array
                  items:
                    type: object
                    required:
                      - query
                      - title
                    properties:
                      query:
                        allOf:
                          - $ref: '#/components/schemas/OmniQuery'
                        description: The query on the document.
                      title:
                        type: string
                        description: The title of the query on the document.
                      topicName:
                        type: string
                        description: The topic the query belongs to. Used to scope topic selection.
                      chartType:
                        type: string
                        description: The chart type used to display the query's results on the document.
                      csvResult:
                        type: string
                        description: CSV-formatted results of the query, if available.
                      description:
                        type: string
                        description: Description of the query shown on the document.
                      displayMode:
                        type: string
                        enum:
                          - both
                          - chart
                          - results
                        description: Whether the document shows the chart, the results table, or both.
                      queryIdentifierMapKey:
                        type: string
                        description: Internal identifier mapping key for the query.
                      queryPresentationId:
                        type: string
                        description: Identifier for how the query is presented on the document.
                      queryPresentationType:
                        type: string
                        description: The type of content the query is presented as on the document.
                      resultWasTruncated:
                        type: boolean
                        description: Whether the query's results were truncated when run.
                      runError:
                        type: string
                        description: The error message from the query's last run, if it failed.
                      runState:
                        type: string
                        enum:
                          - complete
                          - error
                          - running
                          - unrun
                        description: The state of the query's last run.
                      visConfig:
                        type: object
                        description: Visualization configuration for the query's chart.
                  description: Optional list of the queries on the document the prompt is about. Each query's `topicName` scopes topic selection.
                queryAllViews:
                  type: boolean
                  default: false
                  description: |
                    When `true` and the model's [`query_all_views_and_fields`](/modeling/models/parameters/ai-settings/query-all-views-and-fields) setting is enabled, allows the AI to query views that are not included in topics. If the setting is disabled, this parameter has no effect.

                    When both the API parameter and model setting are enabled:
                    
                    - The AI can select from any view in the model, including views not in topics
                    - The AI will prefer topic-organized views unless the question clearly targets a standalone view

                    **Note:** Users with topic-locked permissions cannot use this parameter, even if the model setting is enabled.
                requestedLlm:
                  type: object
                  properties:
                    model:
                      $ref: '#/components/schemas/AiLlmModel'
                    provider:
                      $ref: '#/components/schemas/AiLlmModel'
                      description: |
                        The LLM provider.
                    thinking:
                      $ref: '#/components/schemas/AiLlmThinkingLevels'
                      description: |
                        The thinking level of the LLM. Omit to use the organization default's level.
                        
                        Not all LLMs support this option - if included for an LLM that doesn't support it, the API returns an error.
                  required:
                    - model
                    - provider
                  description: |
                    Optional LLM to generate the query with. The user running the query must have access to the LLM or the API returns an error. Use the [List available LLMs](/api/ai/list-available-llms) endpoint to retrieve a list of available LLMs.

                    Omit to use the organization's default LLM.
                  example:
                    model: claude-sonnet-5
                    provider: bedrock
                    thinking: medium
            example:
              modelId: "123e4567-e89b-12d3-a456-426614174000"
              currentTopicName: "orders"
              prompt: "Show me total revenue by product category for the last quarter"
      responses:
        '200':
          description: |
            Successfully generated query.

            When your organization is over its AI credit threshold, the response also includes `downgradedLlm`, naming the LLM the query was generated on, and the deprecated `downgradedModelTier`, always `haiku`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  topic:
                    type: string
                    description: The name of the topic used for query generation. This will be populated if the AI used a topic and null if the query was generated using a base view.
                    example: "order_items"
                  baseView:
                    type: string
                    description: The name of the base view used for query generation. This will be populated if the AI used a view outside of a topic and null if the query was generated using a topic.
                    example: "users"
                  downgradedLlm:
                    type: string
                    description: |
                      The LLM the query was generated on, present only when your organization is over its AI credit threshold. This is the LLM Omni runs simple tasks on within your organization's LLM family.

                      Absent when no downgrade occurred.
                    example: claude-haiku-5-5
                  downgradedModelTier:
                    type: string
                    deprecated: true
                    description: |
                      Deprecated. Use `downgradedLlm` instead, which names the specific LLM.

                      Present only when your organization is over its AI credit threshold. Always the literal `haiku`.

                      Absent when no downgrade occurred.
                    example: haiku
                  query:
                    type: object
                    properties:
                      model_job:
                        type: object
                        description: A structured Omni query object that can be used with the [Query run API](/api/queries/run-query)
                        properties:
                          model_id:
                            type: string
                            format: uuid
                            description: Model identifier
                          table:
                            type: string
                            description: Base table/topic name
                          fields:
                            type: array
                            items:
                              type: string
                            description: Array of field names to include in query
                          calculations:
                            type: array
                            items:
                              type: object
                            description: Custom calculations
                          filters:
                            type: object
                            description: Filter conditions
                          sorts:
                            type: array
                            items:
                              type: object
                              properties:
                                column_name:
                                  type: string
                                sort_descending:
                                  type: boolean
                                is_column_sort:
                                  type: boolean
                                null_sort:
                                  type: string
                            description: Sort specifications
                          limit:
                            oneOf:
                              - type: integer
                              - type: "null"
                            description: Result row limit
                          pivots:
                            type: array
                            items:
                              type: object
                            description: Pivot configurations
                          fill_fields:
                            type: array
                            items:
                              type: string
                            description: Fields to fill
                          column_totals:
                            type: object
                            description: Column totals configuration
                          row_totals:
                            type: object
                            description: Row totals configuration
                          column_limit:
                            type: integer
                            description: Column limit for pivots
                          default_group_by:
                            type: boolean
                            description: Enable default grouping
                          join_via_map:
                            type: object
                            description: Join configuration map
                          join_paths_from_topic_name:
                            type: string
                            description: Topic name for join paths
                          version:
                            type: integer
                            description: Query version
                          period_over_period_computations:
                            type: array
                            items:
                              type: object
                            description: Period over period calculations
                          query_references:
                            type: object
                            description: Query references
                          metadata:
                            type: object
                            description: Query metadata
                          custom_summary_types:
                            type: object
                            description: Custom summary type definitions
              example:
                query:
                  model_job:
                    model_id: "bcf0cffd-ec1b-44d5-945a-a261ebe407fc"
                    table: "order_items"
                    fields:
                      - "products.item_name"
                      - "order_items.total_sale_price"
                    calculations: []
                    filters: {}
                    sorts:
                      - column_name: "order_items.total_sale_price"
                        sort_descending: true
                        is_column_sort: false
                        null_sort: "OMNI_DEFAULT"
                    limit: 10
                    pivots: []
                    fill_fields: []
                    column_totals: {}
                    row_totals: {}
                    column_limit: 50
                    default_group_by: true
                    join_via_map: {}
                    join_paths_from_topic_name: "order_items"
                    version: 5
                    period_over_period_computations: []
                    query_references: {}
                    metadata: {}
                    custom_summary_types: {}
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Invalid method`
            - `Invalid JSON`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Feature not enabled`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '422':
          $ref: '#/components/responses/AiRequestedLlmError'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/pick-topic:
    post:
      tags:
        - AI
      summary: Pick topic
      description: |
        Analyze a natural language prompt and determines which topic in the model is the best fit for answering the question. 
        
        Useful as a preprocessing step before calling the [Generate AI query](/api/ai/generate-a-query) or [Create AI job](/api/ai/create-ai-job) endpoints, especially when the user's question could relate to multiple topics.
      security:
        - bearerAuth: []
      operationId: aiPickTopic
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AiPickTopicBody'
      responses:
        '200':
          description: Topic selected successfully. The returned `topicId` can be used as the `topicName` parameter in other AI endpoints.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AiPickTopicResponse'
        '400':
          description: Invalid request body. The `prompt` or `modelId` may be missing or malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '402':
          description: |
            AI is unavailable because the organization, the acting user (the `userId` override when provided), or that user's embed entity is over its AI credit limit. The body carries the stable reason code `shutoff`.
          content:
            application/json:
              schema:  # AiCreditShutoffError
                type: object
                required:
                  - code
                  - detail
                  - status
                properties:
                  code:
                    type: string
                    enum:
                      - shutoff
                    description: Stable reason code identifying an AI-credit shutoff.
                    example: shutoff
                  detail:
                    type: string
                    description: Human-readable error message describing what went wrong.
                    example: The AI credit limit has been reached. Contact your administrator for assistance.
                  status:
                    type: integer
                    description: HTTP status code of the error.
                    example: 402
        '403':
          description: Insufficient permissions. Requires the **Querier** role on the specified model.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: The specified model was not found, or no accessible topics exist in the model.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          description: AI service error.

  /v1/ai/conversations:
    get:
      tags:
        - AI
      summary: List AI conversations
      description: |
        List a user's recent AI conversations, ordered by most-recent activity. Each conversation includes an ID that can be passed as `conversationId` to subsequent [Create AI job](/api/ai/create-ai-job) requests to continue the thread.

        Returns a paginated list with conversation metadata including optional names and the most recent user prompt for display purposes.
      security:
        - bearerAuth: []
      operationId: listAIConversations
      parameters:
        - name: userId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: |
            **Requires an Organization API key.** Filter conversations to a specific user by their membership ID.

            User-scoped tokens automatically see only their own conversations and cannot use this parameter. Organization API keys can optionally filter by user or see all conversations in the organization.
          example: 9e8719d9-276a-4964-9395-a493189a247c
        - name: pageSize
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
          description: Number of conversations to return per page.
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Pagination cursor from a previous response's `pageInfo.nextCursor`. Use to fetch the next page of results.
      responses:
        '200':
          description: Successfully retrieved conversations
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - pageInfo
                properties:
                  data:
                    type: array
                    description: List of AI conversations
                    items:
                      type: object
                      required:
                        - id
                        - updatedAt
                        - createdAt
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: Unique identifier for the conversation. Pass this as `conversationId` when creating AI jobs to continue the thread.
                        name:
                          type: string
                          nullable: true
                          description: Optional name for the conversation. May be `null` if no name has been set.
                        lastUserPrompt:
                          type: string
                          nullable: true
                          description: The most recent user prompt in the conversation, suitable for display as a one-line summary. May be `null` for empty conversations.
                        updatedAt:
                          type: string
                          format: date-time
                          description: Timestamp of the most recent activity in the conversation in ISO 8601 format.
                        createdAt:
                          type: string
                          format: date-time
                          description: Timestamp when the conversation was created in ISO 8601 format.
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
              example:
                data:
                  - id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    name: "Q1 Revenue Analysis"
                    lastUserPrompt: "Show me revenue trends by region for Q1"
                    updatedAt: "2026-05-15T14:32:10Z"
                    createdAt: "2026-05-15T09:15:30Z"
                  - id: "c3d4e5f6-a7b8-9012-cdef-123456789012"
                    name: null
                    lastUserPrompt: "What were our top selling products last month?"
                    updatedAt: "2026-05-14T16:20:45Z"
                    createdAt: "2026-05-14T16:20:45Z"
                pageInfo:
                  hasNextPage: false
                  nextCursor: null
                  pageSize: 50
                  totalRecords: 2
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Invalid userId format` - `userId` must be a valid UUID
            - `Invalid pageSize` - `pageSize` must be an integer between 1 and 100
            - `Invalid nextCursor` - pagination `cursor` is malformed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:
            - `Insufficient permissions` - User lacks ability to use AI on any model in the organization
            - `User-scoped tokens cannot filter by userId` - Personal Access Tokens cannot use the `userId` parameter
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:
            - `User not found` - the specified `userId` does not exist in the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/conversations/{conversationId}:
    get:
      tags:
        - AI
      summary: Get conversation
      description: |
        Retrieve a conversation with its full message history — alternating user and assistant turns. Each assistant turn carries the originating `jobId` and an `omniChatUrl` deep link.

        The conversations returned by this API depend on the type of API key being used:

        - Organization API keys can access any conversation in the organization
        - Personal Access Tokens can only access the authenticating user's conversations
      security:
        - bearerAuth: []
      operationId: getConversation
      parameters:
        - name: conversationId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the conversation
      responses:
        '200':
          description: Conversation retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                  - userId
                  - organizationId
                  - createdAt
                  - updatedAt
                  - messages
                properties:
                  id:
                    type: string
                    format: uuid
                    description: The unique identifier for this conversation.
                    example: "660e8400-e29b-41d4-a716-446655440001"
                  userId:
                    type: string
                    format: uuid
                    description: The user ID who created this conversation.
                    example: "990e8400-e29b-41d4-a716-446655440004"
                  organizationId:
                    type: string
                    format: uuid
                    description: The organization that owns this conversation.
                    example: "880e8400-e29b-41d4-a716-446655440003"
                  createdAt:
                    type: string
                    format: date-time
                    description: When the conversation was created.
                    example: "2025-01-15T10:00:00.000Z"
                  updatedAt:
                    type: string
                    format: date-time
                    description: When the conversation was last modified.
                    example: "2025-01-15T10:01:30.000Z"
                  messages:
                    type: array
                    description: Ordered list of messages in the conversation, alternating between user and assistant turns.
                    items:
                      type: object
                      required:
                        - role
                        - content
                        - timestamp
                      properties:
                        role:
                          type: string
                          enum: [user, assistant]
                          description: The role of the message sender.
                          example: "user"
                        content:
                          type: string
                          description: The message content. For user messages, this is the prompt. For assistant messages, this is the AI's response in Markdown format.
                          example: "What are the top 5 products by revenue?"
                        timestamp:
                          type: string
                          format: date-time
                          description: When this message was created.
                          example: "2025-01-15T10:00:00.000Z"
                        jobId:
                          type: string
                          format: uuid
                          description: |
                            **Only present for assistant messages.** The ID of the AI job that generated this response. Use this with the [Get AI job status](/api/ai/get-ai-job-status) endpoint to retrieve job details.
                          example: "550e8400-e29b-41d4-a716-446655440000"
                        omniChatUrl:
                          type: string
                          format: uri
                          description: |
                            **Only present for assistant messages.** URL to view this conversation in the Omni chat interface. Opens the chat session where the job actions and results are visible.
                          example: "https://my-org.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
              examples:
                conversation:
                  summary: A conversation with multiple turns
                  value:
                    id: "660e8400-e29b-41d4-a716-446655440001"
                    userId: "990e8400-e29b-41d4-a716-446655440004"
                    organizationId: "880e8400-e29b-41d4-a716-446655440003"
                    createdAt: "2025-01-15T10:00:00.000Z"
                    updatedAt: "2025-01-15T10:05:00.000Z"
                    messages:
                      - role: "user"
                        content: "What are the top 5 products by revenue?"
                        timestamp: "2025-01-15T10:00:00.000Z"
                      - role: "assistant"
                        content: "### Top 5 Products by Revenue\n\n1. **Sunglasses** - $678,994\n2. **Jeans** - $475,072\n3. **T-Shirt** - $320,150\n4. **Sneakers** - $289,033\n5. **Hat** - $201,487"
                        timestamp: "2025-01-15T10:01:30.000Z"
                        jobId: "550e8400-e29b-41d4-a716-446655440000"
                        omniChatUrl: "https://my-org.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
                      - role: "user"
                        content: "Show me revenue by month for sunglasses"
                        timestamp: "2025-01-15T10:03:00.000Z"
                      - role: "assistant"
                        content: "### Sunglasses Revenue by Month\n\n- January: $52,430\n- February: $48,221\n- March: $61,992"
                        timestamp: "2025-01-15T10:05:00.000Z"
                        jobId: "550e8400-e29b-41d4-a716-446655440010"
                        omniChatUrl: "https://my-org.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Invalid conversation ID format. Must be a valid UUID.`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: |
            Unauthorized

            Possible error messages:
            - `Missing or invalid API key`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:
            - `User does not have USE_AI permission`
            - `User-scoped token cannot access another user's conversation`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Conversation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: "404"
                detail: "Conversation not found"
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/credit-controls:
    get:
      description: |
        <Note>
          **Organization Admin** permissions are required for this endpoint.
        </Note>

        Get the organization's AI credit controls: the downgrade and shutoff thresholds, the default per-user and per-entity-group credit limits, plus read-only context (the credit limit, usage for the current billing period, and the period bounds). This endpoint can be used for embedded instances.
      operationId: aiCreditControlsGet
      summary: Get AI credit controls
      tags:
        - AI Credit Controls
      responses:
          '200':
            description: Current credit controls. Thresholds are `null` when the corresponding control is off.
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/AiCreditControlsResponse'
          '401':
            description: Missing or invalid API key.
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ApiError401'
          '403':
            description: Insufficient permissions.
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ApiError403'
          '429':
            $ref: '#/components/responses/TooManyRequests'
          '500':
            $ref: '#/components/responses/InternalServerError'

    patch:
      description: |
        <Note>
          **Organization Admin** permissions are required for this endpoint.
        </Note>

        Update the organization's AI credit controls. This endpoint can be used for embedded instances.
        
        All fields are optional, but at least one must be provided:

        - Omit a field to leave it unchanged
        - Send `null` to turn that control off (for `userDefaultCredits` and `entityGroupDefaultCredits`: unlimited by default)
        - Send a non-negative number to set it
      operationId: aiCreditControlsUpdate
      summary: Update AI credit controls
      tags:
        - AI Credit Controls
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                downgradeCredits:
                  type: number
                  nullable: true
                  minimum: 0
                  description: Credit usage at which AI downgrades to a cheaper model. Omit to leave unchanged, `null` to turn off, or a non-negative number to set. Must be less than or equal to `shutoffCredits`.
                  example: 800
                shutoffCredits:
                  type: number
                  nullable: true
                  minimum: 0
                  description: Credit usage at which AI shuts off entirely. Omit to leave unchanged, `null` to turn off, or a non-negative number to set.
                  example: 1200
                userDefaultCredits:
                  type: number
                  nullable: true
                  minimum: 0
                  description: Default per-user AI credit limit for the billing period. This limit is what what every user without an individual limit gets. Omit to leave unchanged, `null` for unlimited by default, or a non-negative number to set.
                  example: 100
                entityGroupDefaultCredits:
                  type: number
                  nullable: true
                  minimum: 0
                  description: Default per-entity-group AI credit limit for the billing period. This limit is what every embed entity group without an individual limit gets. Omit to leave unchanged, `null` for unlimited by default, or a non-negative number to set.
                  example: 100
      responses:
        '200':
          description: Thresholds updated. Returns the full current state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AiCreditControlsResponse'
        '400':
          description: |
            Invalid request. Common causes:

            - Empty body
            - A negative value
            - An unknown field
            - `downgradeCredits` greater than `shutoffCredits`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/credit-controls/users:
    get:
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        List the organization's active individual user AI credit limits, ordered by `userId` ascending.

        Only users with an individual limit will be returned in the response.
      operationId: aiCreditControlsUsersList
      summary: List individual users' AI credit limits
      tags:
        - AI Credit Controls
      parameters:
        - schema:
            type: string
            description: Cursor for pagination from previous response `nextCursor`
            example: eyJpZCI6IjEyMzQ1In0
          required: false
          description: Cursor for pagination from previous response `nextCursor`
          name: cursor
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
            description: Number of results per page
            example: 20
          required: false
          description: Number of results per page
          name: pageSize
          in: query
      responses:
        '200':
          description: One page of users' individual AI credit limits.
          content:
            application/json:
              schema:
                type: object
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    items:
                      type: object
                      properties:
                        creditLimit:
                          type: number
                          nullable: true
                          minimum: 0
                          description: The user's individual AI credit limit, or `null` for an explicit unlimited override.
                          example: 50
                        userId:
                          type: string
                          description: The user's id within this organization.
                          example: f4a2b3c8-0d1e-4f5a-9b6c-7d8e9f0a1b2c
                      required:
                        - creditLimit
                        - userId
                    description: Users with an individual AI credit limit, ordered by `userId` ascending.
                required:
                  - pageInfo
                  - records
              examples:
                users_with_limits:
                  summary: Example response with users
                  value:
                    pageInfo:
                      nextCursor: eyJpZCI6IjEyMzQ1In0
                      hasNextPage: true
                    records:
                      - userId: f4a2b3c8-0d1e-4f5a-9b6c-7d8e9f0a1b2c
                        creditLimit: 50
                      - userId: a7b8c9d0-e1f2-3a4b-5c6d-7e8f9a0b1c2d
                        creditLimit: null
        '400':
          description: Invalid cursor or pageSize.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
    patch:
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Set individual users' AI credit limits in bulk. Each entry names a user and either sets an individual limit or removes one so the user follows the organization default. All entries are applied in a single transaction.
      operationId: aiCreditControlsUsersUpdate
      summary: Set individual users' AI credit limits
      tags:
        - AI Credit Controls
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                users:
                  type: array
                  items:
                    type: object
                    properties:
                      creditLimit:
                        type: number
                        nullable: true
                        minimum: 0
                        description: The user's individual AI credit limit for the billing period, or `null` for unlimited. This will override the organization default. Mutually exclusive with `useDefaultLimit`.
                        example: 50
                      useDefaultLimit:
                        type: boolean
                        enum:
                          - true
                        description: Removes the user's individual limit so they follow the organization default. Mutually exclusive with `creditLimit`.
                      userId:
                        type: string
                        description: The user's ID within this organization.
                        example: f4a2b3c8-0d1e-4f5a-9b6c-7d8e9f0a1b2c
                    required:
                      - userId
                    additionalProperties: false
                  minItems: 1
                  maxItems: 1000
                  description: 'Users to update, at most 1000 per request. Each entry has a `userId` plus exactly one of `creditLimit` (number or `null`) or `useDefaultLimit: true`.'
              required:
                - users
              additionalProperties: false
            examples:
              set_limits:
                summary: Set limits for two users
                value:
                  users:
                    - userId: f4a2b3c8-0d1e-4f5a-9b6c-7d8e9f0a1b2c
                      creditLimit: 50
                    - userId: a7b8c9d0-e1f2-3a4b-5c6d-7e8f9a0b1c2d
                      creditLimit: null
              reset_to_default:
                summary: Remove individual limit
                value:
                  users:
                    - userId: f4a2b3c8-0d1e-4f5a-9b6c-7d8e9f0a1b2c
                      useDefaultLimit: true
      responses:
        '200':
          description: All entries applied. Returns each user's effective limit, in request order.
          content:
            application/json:
              schema:
                type: object
                properties:
                  users:
                    type: array
                    items:
                      type: object
                      properties:
                        creditLimit:
                          type: number
                          nullable: true
                          minimum: 0
                          description: The user's effective AI credit limit, or `null` for unlimited.
                          example: 50
                        userId:
                          type: string
                          description: The user's ID within this organization.
                          example: f4a2b3c8-0d1e-4f5a-9b6c-7d8e9f0a1b2c
                        usesDefaultLimit:
                          type: boolean
                          description: |
                            If `true`, the user has no individual limit and follows the organization default.
                      required:
                        - creditLimit
                        - userId
                        - usesDefaultLimit
                
        '400':
          description: |
            Invalid request. Common causes:

            - An empty users array
            - More than 1000 entries
            - An entry with both `creditLimit` and `useDefaultLimit` (or neither)
            - A negative `creditLimit`
            - A duplicated `userId`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions, or credit controls editing is disabled for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Specified `userId` is not a member of the organization; the response names the first invalid ID. No limits are changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/credit-controls/entity-groups:
    get:
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        List the organization's active individual embed entity-group AI credit limits, ordered by entity group id ascending.

        Only entity groups with an individual limit will be returned in the response. Entity groups that use the organization default are not included.
      operationId: aiCreditControlsEntityGroupsList
      summary: List individual entity groups' AI credit limits
      tags:
        - AI Credit Controls
      parameters:
        - schema:
            type: string
            description: Cursor for pagination from previous response `nextCursor`
            example: eyJpZCI6IjEyMzQ1In0
          required: false
          description: Cursor for pagination from previous response `nextCursor`
          name: cursor
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
            description: Number of results per page
            example: 20
          required: false
          description: Number of results per page
          name: pageSize
          in: query
      responses:
        '200':
          description: One page of entity groups' individual AI credit limits.
          content:
            application/json:
              schema:
                type: object
                required:
                  - pageInfo
                  - records
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    description: Entity groups with an individual AI credit limit, ordered by entity group ID.
                    items:
                      type: object
                      properties:
                        creditLimit:
                          type: number
                          nullable: true
                          minimum: 0
                          description: The entity group's individual AI credit limit, or `null` for an explicit unlimited override.
                          example: 50
                        entity:
                          type: string
                          description: The embed entity's identifier (the SSO `entity` value).
                          example: blobsrus
                      required:
                        - creditLimit
                        - entity
              examples:
                entityGroupsWithLimits:
                  summary: Example response with entity groups
                  value:
                    pageInfo:
                      nextCursor: eyJpZCI6IjEyMzQ1In0
                      hasNextPage: true
                    records:
                      - entity: blobsrus
                        creditLimit: 50
                      - entity: blobsrus-eu
                        creditLimit: null
        '400':
          description: |
            Invalid cursor or `pageSize`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
    patch:
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Set individual embed entity groups' AI credit limits in bulk. Each entry names an entity group and either sets an individual limit or removes one so the entity group follows the organization default. All entries are applied in a single transaction.
      x-mint:
        content: |
          <Tip>
            To set an unlimited override for an entity group, use `creditLimit: null`. This is distinct from having the entity group follow the organization default.
          </Tip>
      operationId: aiCreditControlsEntityGroupsUpdate
      summary: Set individual entity groups' AI credit limits
      tags:
        - AI Credit Controls
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - entityGroups
              additionalProperties: false
              properties:
                entityGroups:
                  type: array
                  minItems: 1
                  maxItems: 1000
                  description: |
                    Entity groups to update. Each entry has an `entity` plus exactly one of `creditLimit` (number or `null`) or `useDefaultLimit: true`.
                  items:
                    type: object
                    required:
                      - entity
                    additionalProperties: false
                    properties:
                      creditLimit:
                        type: number
                        nullable: true
                        minimum: 0
                        description: The entity group's individual AI credit limit for the billing period, or `null` for unlimited. This will override the organization default. Mutually exclusive with `useDefaultLimit`.
                        example: 50
                      entity:
                        type: string
                        description: The embed entity's identifier (the SSO `entity` value).
                        example: blobsrus
                      useDefaultLimit:
                        type: boolean
                        description: Removes the entity group's individual limit so it follows the organization default. Mutually exclusive with `creditLimit`.
            examples:
              'Set limits for 2 entity groups':
                summary: Set limits for two entity groups
                value:
                  entityGroups:
                    - entity: blobsrus
                      creditLimit: 50
                    - entity: blobsrus-eu
                      creditLimit: null
              'Remove individual limit':
                summary: Remove individual limit
                value:
                  entityGroups:
                    - entity: blobsrus
                      useDefaultLimit: true
      responses:
        '200':
          description: All entries applied. Returns each entity group's effective limit, in request order.
          content:
            application/json:
              schema:
                type: object
                required:
                  - entityGroups
                properties:
                  entityGroups:
                    type: array
                    items:
                      type: object
                      required:
                        - creditLimit
                        - entity
                        - usesDefaultLimit
                      properties:
                        creditLimit:
                          type: number
                          nullable: true
                          minimum: 0
                          description: The entity group's effective AI credit limit, or `null` for unlimited.
                          example: 50
                        entity:
                          type: string
                          description: The embed entity's identifier (the SSO `entity` value).
                          example: blobsrus
                        usesDefaultLimit:
                          type: boolean
                          description: |
                            If `true`, the entity group has no individual limit and follows the organization default.
        '400':
          description: |
            Invalid request. Common causes:

            - An empty `entityGroups` array
            - More than 1000 entries
            - An entry with both `creditLimit` and `useDefaultLimit`, or neither
            - A negative `creditLimit`
            - A duplicated `entity`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions, or credit controls editing is disabled for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: An entity has no entity group in the organization; the response names the first invalid entity. No limits are changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/credit-usage/users:
    post:
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        List individual users' (up to 1,000 unique users per request) AI credit usage for the current billing period. Each user ID must be a member of the organization. Unknown, invalid, and duplicate IDs will fail the request.
        
        Returns each user's credits used, in request order, plus the billing-period bounds. Users with no current usage will report `0`. 
      operationId: aiCreditUsageUsersRead
      summary: Read individual users' AI credit usage
      tags:
        - AI Credit Usage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userIds
              additionalProperties: false
              properties:
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                    example: f4a2b3c8-0d1e-4f5a-9b6c-7d8e9f0a1b2c
                  minItems: 1
                  maxItems: 1000
                  description: |
                    Users to read usage for, at most 1000 per request. Each entry is a user's ID within this organization; each ID must be included only once. Duplicated IDs will result in an error.
      responses:
        '200':
          description: Each requested user's credit usage for the current billing period, in request order.
          content:
            application/json:
              schema:
                type: object
                required:
                  - periodEnd
                  - periodStart
                  - users
                properties:
                  periodEnd:
                    type: integer
                    minimum: 0
                    description: End of the current billing period as a Unix ms timestamp (UTC calendar-month boundary).
                  periodStart:
                    type: integer
                    minimum: 0
                    description: Start of the current billing period as a Unix ms timestamp (UTC calendar-month boundary).
                  users:
                    type: array
                    items:
                      type: object
                      required:
                        - creditsUsed
                        - userId
                      properties:
                        creditsUsed:
                          type: number
                          minimum: 0
                          description: Credits the user consumed in the current billing period. `0` when there is no usage yet.
                          example: 42
                        userId:
                          type: string
                          format: uuid
                          description: The user's ID within this organization.
                          example: f4a2b3c8-0d1e-4f5a-9b6c-7d8e9f0a1b2c
        '400':
          description: |
            Invalid request. Common causes: an empty `userIds` array, more than 1000 IDs, or a duplicated `userId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: A `userId` is not a member of the organization; the response names the first invalid ID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'

  /v1/ai/credit-usage/entity-groups:
    post:
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        List individual embed entity groups' (up to 1,000 unique entities per request) AI credit usage for the current billing period. Each included `entity` must have an entity group in the organization. Unknown, invalid, and duplicate `entity` values will fail the request.

        Returns each entity group's credits used, in request order, plus the billing-period bounds. Entity groups with no current usage will report `0`.
      operationId: aiCreditUsageEntityGroupsRead
      summary: Read individual entity groups' AI credit usage
      tags:
        - AI Credit Usage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - entities
              additionalProperties: false
              properties:
                entities:
                  type: array
                  items:
                    type: string
                    example: blobsrus
                  minItems: 1
                  maxItems: 1000
                  description: |
                    Entity groups to read usage for, at most 1000 per request. Each entry is an embed `entity` (the SSO `entity` value) corresponding to an entity group in the organization. Each entity must be included only once, as duplicated entries will fail the request.
      responses:
        '200':
          description: Each requested entity group's credit usage for the current billing period, in request order.
          content:
            application/json:
              schema:
                type: object
                required:
                  - periodEnd
                  - periodStart
                  - entityGroups
                properties:
                  periodEnd:
                    type: integer
                    minimum: 0
                    description: End of the current billing period as a Unix ms timestamp (UTC calendar-month boundary).
                  periodStart:
                    type: integer
                    minimum: 0
                    description: Start of the current billing period as a Unix ms timestamp (UTC calendar-month boundary).
                  entityGroups:
                    type: array
                    items:
                      type: object
                      required:
                        - creditsUsed
                        - entity
                      properties:
                        creditsUsed:
                          type: number
                          minimum: 0
                          description: Credits the entity group consumed in the current billing period. `0` when it has no usage yet.
                          example: 42
                        entity:
                          type: string
                          description: The embed entity's identifier (the SSO `entity` value).
                          example: blobsrus
        '400':
          description: |
            Invalid request. Common causes: an empty `entities` array, more than 1000 `entities`, or a duplicated entity.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: An entity has no entity group in the organization; the response names the first invalid entity.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'

  /v1/ai/jobs:
    post:
      tags:
        - AI
      summary: Create AI job
      description: |
        Submit a new AI job for asynchronous execution. The AI will analyze the prompt, generate and execute queries against the specified model, and produce a summarized answer.

        Jobs are processed by a background worker and typically complete within 15–60 seconds. The AI chat returned in the response will remain blank until the AI is finished processing.
        
        Use [Get AI job status](/api/ai/get-ai-job-status) to poll for status, or configure a `webhookUrl` to receive a notification when the job completes. Optionally continue an existing conversation by providing a `conversationId`.
      x-mint:
        content: |
          ## Running a skill

          Including a skill's `/identifier` anywhere in `prompt` runs that saved [skill](/ai/skills).

          ## Dashboard creation and publishing

          The AI can build and modify dashboards. A dashboard it creates is published immediately. An edit to an existing dashboard lands on that dashboard's draft. The AI can publish the draft when the prompt explicitly requests it (for example, _"fix the revenue tile and publish it"_). Publishing makes the draft live for everyone who views the dashboard.

          <Note>
            Publishing takes any changes already in the draft live with it, not just the edits made by the AI in this job. The AI's response will disclose when it published a dashboard and when that publish included draft changes made by someone else.
          </Note>

          A publish request may be refused when:

          - No draft exists on the dashboard
          - The draft is stale, meaning it has been superseded by a more recent published version
          - The organization requires pull requests to merge branch changes
          - Publishing would disable the dashboard's scheduled deliveries. In the browser, this requires user confirmation; headless jobs refuse rather than disable deliveries silently.

          ## Choosing the LLM

          By default, a job runs on the organization's default LLM and thinking level. Pass `requestedLlm` to pin the job to a different one instead. 

          Use [List available LLMs](/api/ai/list-available-llms) to retrieve a list of the LLMs the user the job runs as may pick. A pick that user isn't allowed to make returns a an error naming only the refused LLM.

          ## Webhook notifications

          If a `webhookUrl` is configured, it will receive a `POST` with the following body when the job reaches a terminal state:

          ```json
          {
            "event_id": "evt_550e8400_COMPLETE_1706123456",
            "jobId": "550e8400-...",
            "status": "COMPLETE",
            "result_summary": "Generated revenue report showing $1.2M total revenue",
            "metadata": { "slack_channel": "C123456" },
            "completed_at": "2025-01-24T10:30:00Z"
          }
          ```

          The request will also include the following signature headers:
          
          - `X-Omni-Signature-Timestamp` - `unix`
          - `X-Omni-Signature` - (`sha256=HMAC(timestamp.body, secret)`)

          <Note>
            The webhook payload includes `result_summary` which can contain actual data values from query results. Full result detail (CSV data, query definitions, actions) requires calling the [Stream AI job results endpoint](/api/ai/stream-ai-job-results) with credentials.
          </Note>
      security:
        - bearerAuth: []
      operationId: createAIJob
      parameters:
        - name: userId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: |
            **Requires an Organization API key.** The ID of the user to run the query as.

            Personal Access Tokens (PATs) cannot use this parameter to act on behalf of other users.
          example: 9e8719d9-276a-4964-9395-a493189a247c
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - modelId
                - prompt
              properties:
                modelId:
                  type: string
                  format: uuid
                  description: The ID of the model to run the AI job against
                prompt:
                  type: string
                  description: |
                    Natural language instruction describing the desired task. Include a skill's `/identifier` to run that [skill](/ai/skills).
                conversationId:
                  type: string
                  format: uuid
                  description: |
                    Optional conversation ID to continue an existing conversation.

                    With a Personal Access Token or MCP OAuth grant, only the conversation's owner can continue it; any other caller gets a `404`.
                branchId:
                  type: string
                  format: uuid
                  description: |
                    Optional branch ID for the model. Must be a branch of the shared model specified by `modelId`.
                  
                    Queries run against the branch model, and if the Omni Agent makes model changes, they are written to this branch instead of a newly created one. If omitted and the Agent makes model changes, a new branch is created automatically.
                topicName:
                  type: string
                  description: |
                    Topic name to scope query generation.
                    
                    If not provided, the AI will automatically select the best topic based on the prompt. For multi-turn conversations, the AI will carry forward the topic from the previous turn.
                    
                    The topic must exist in the model and be allowed by any [`ai_chat_topics`](/modeling/models/ai-chat-topics) restrictions or the API will return a `404`. Use the [Pick topic endpoint](/api/ai/pick-topic) to programmatically determine the right topic.
                requestedLlm:
                  type: object
                  properties:
                    model:
                      type: string
                      minLength: 1
                      maxLength: 512
                    provider:
                      type: string
                      enum:
                        - bedrock
                        - anthropic
                        - openai
                        - grok
                        - snowflake_cortex
                        - openai_compatible
                        - fireworks
                        - fireworks_managed
                        - databricks_genie
                        - vertex_ai
                    thinking:
                      type: string
                      enum:
                        - default
                        - none
                        - low
                        - medium
                        - high
                  required:
                    - model
                    - provider
                  description: |
                    Optional LLM to run the job on, as `{ provider, model, thinking? }`. Must be one the organization has approved under AI Hub → LLM settings and that the user the job runs as may pick: [test-only](/ai/settings/llm-settings#test-only) LLMs are for organization admins only, and embedded users can't pick one. Anything else returns a `422`; [List available LLMs](/api/ai/list-available-llms) lists the LLMs that user may pick. Omit to run on the organization default.

                    `model` is the LLM's catalog name on an Omni-hosted provider (e.g. `claude-sonnet-5`) or the id the admin entered for a bring-your-own-key provider. `thinking` is one of `default`, `none`, `low`, `medium`, `high`; which of them an LLM honors depends on the LLM, and one it does not is rejected. Omit `thinking` to run at the organization default's level where the LLM honors it, else `default`.
                  example:
                    model: claude-sonnet-5
                    provider: bedrock
                    thinking: medium
                progressWebhookEnabled:
                  type: boolean
                  description: |
                    When `true`, real-time progress events are sent using `POST` to the defined `webhookUrl` during execution (e.g., _"Searching for revenue fields"_, _"Query returned 42 rows"_). **`webhookUrl` is required if enabled.** 
                    
                    Progress events are best-effort: single attempt, no retries, failures do not affect job execution.
                webhookUrl:
                  type: string
                  format: uri
                  description: |
                    An optional webhook URL to receive webhook updates. Always receives a terminal event (job.complete or job.failed) when the job finishes.
                    
                    When `progressWebhookEnabled: true`, also receives real-time progress events during execution. **Required if `progressWebhookEnabled: true`.**
                webhookSigningSecret:
                  type: string
                  description: An HMAC-SHA256 secret for signing webhook payloads
                webhookMetadata:
                  type: object
                  description: Opaque metadata passed back unchanged in webhook payloads
                attachments:
                  type: array
                  maxItems: 5
                  description: |
                    Optional image or PDF attachments giving the AI additional visual context alongside the prompt (e.g., a screenshot or export of a legacy Tableau/Power BI/Looker dashboard being migrated).

                    Image files are limited to 3MB each; text files such as CSVs have a ~50KB limit. All attachments in a single request share a combined size budget of roughly 15 MB, shared with your prompt text. Requests can be rejected if the attachments exceed this limit, whether on their own or combined with the prompt. In this case, try attaching fewer files or using smaller/lower-resolution exports.

                    <Note>
                      Attachments are stored internally and never exposed over the public API. They will not appear in job status responses, results, or webhook payloads.
                    </Note>
                  items:
                    type: object
                    required:
                      - data
                      - mimeType
                    properties:
                      data:
                        type: string
                        minLength: 1
                        description: Base64-encoded file content
                        example: "iVBORw0KGgoAAAANSUhEUgAA..."
                      mimeType:
                        type: string
                        description: MIME type of the attachment. Must be an image type (e.g., `image/png`, `image/jpeg`, `image/webp`) or `application/pdf`
                        example: "image/png"
                      name:
                        type: string
                        description: Optional filename, used for display and logging only
                        example: "legacy-dashboard-screenshot.png"
            examples:
              basic:
                summary: Basic AI job
                value:
                  modelId: "123e4567-e89b-12d3-a456-426614174000"
                  prompt: "Analyze sales trends by region for the past year"
              with_attachment:
                summary: AI job with visual context
                value:
                  modelId: "123e4567-e89b-12d3-a456-426614174000"
                  prompt: "Re-create this dashboard in Omni"
                  attachments:
                    - data: "iVBORw0KGgoAAAANSUhEUgAAAAUA..."
                      mimeType: "image/png"
                      name: "tableau-dashboard-export.png"
      responses:
        '201':
          description: AI job created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  jobId:
                    type: string
                    format: uuid
                    description: The unique identifier for the created job
                  conversationId:
                    type: string
                    format: uuid
                    description: The conversation ID associated with this job
                  omniChatUrl:
                    type: string
                    description: |
                      URL to view this conversation in the Omni chat interface. Opens the chat session where the job actions and results are visible. The AI Chat will remain blank until the AI job is finished processing.

                      Omitted for MCP requests (`X-MCP-Request: true`) when the organization hides Omni links from MCP clients.
              example:
                jobId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                conversationId: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                omniChatUrl: "https://blobsrus.omni.co/chat/b2c3d4e5-f6a7-8901-bcde-f12345678901"
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Invalid modelId/branchId/conversationId`
            - `Missing required field: prompt`
            - `Invalid webhook URL format`
            - `Invalid attachment MIME type (must be image or PDF)`
            - `Malformed base64 data in attachment`
            - `Too many attachments (maximum 5)`
            - `Attachment token budget exceeded (50,000 tokens shared with prompt)`
            - `Cannot publish: no draft exists on dashboard`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible error messages:
            - `Feature not enabled`
            - `Insufficient permissions`
            - `User-scoped API keys cannot act on behalf of other users`
            - `Cannot publish: organization requires pull request to merge branch changes`
            - `Cannot publish: would disable dashboard's scheduled deliveries`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:
            - `Model not found`
            - `Topic not found or excluded by ai_chat_topics`
            - `Conversation not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '409':
          description: |
            Conflict

            Possible error messages:
            - `Active job exists for conversation`
            - `Cannot publish: draft is stale or has been superseded`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/AiRequestedLlmError'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/jobs/{jobId}:
    get:
      tags:
        - AI
      summary: Get AI job status
      description: |
        Retrieve the current status and details of an AI job, including its state, progress information, and result summary.

        The response fields vary by `state` — for example, `progress` is only present during `EXECUTING`, and `resultSummary` is only present when `COMPLETE`.

        Poll this endpoint every 2–5 seconds until the job reaches a terminal state (`COMPLETE`, `FAILED`, or `CANCELLED`).
      security:
        - bearerAuth: []
      operationId: aiJobStatus
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the AI job
      responses:
        '200':
          description: Job status retrieved successfully. Check the `state` field to determine if the job is still running or has reached a terminal state.
          content:
            application/json:
              schema:  # AiJobStatusResponse
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: The unique identifier for this job.
                    example: "550e8400-e29b-41d4-a716-446655440000"
                  state:
                    type: string
                    enum: [CANCELLED, COMPLETE, DELIVERING, EXECUTING, FAILED, QUEUED]
                    description: Current state of the job. Terminal states are `COMPLETE`, `FAILED`, and `CANCELLED`. Poll until the job reaches a terminal state.
                    example: "QUEUED"
                  prompt:
                    type: string
                    description: The natural language prompt that was submitted.
                    example: "What are the top 5 products by revenue?"
                  conversationId:
                    type: string
                    format: uuid
                    description: The conversation this job belongs to. Use this to submit follow-up jobs in the same conversation thread.
                    example: "660e8400-e29b-41d4-a716-446655440001"
                  modelId:
                    type: string
                    nullable: true
                    format: uuid
                    description: The shared model ID used for query generation.
                    example: "770e8400-e29b-41d4-a716-446655440002"
                  branchId:
                    type: string
                    nullable: true
                    format: uuid
                    description: Branch ID used for model context, or null if querying the main shared model.
                  organizationId:
                    type: string
                    format: uuid
                    description: The organization that owns this job.
                    example: "880e8400-e29b-41d4-a716-446655440003"
                  userId:
                    type: string
                    format: uuid
                    description: The user ID who created (or is associated with) this job.
                    example: "990e8400-e29b-41d4-a716-446655440004"
                  topicName:
                    type: string
                    nullable: true
                    description: Topic name used to scope query generation, or null if the AI selected the topic automatically.
                    example: "order_items"
                  requestedLlm:
                    type: object
                    nullable: true
                    properties:
                      model:
                        $ref: '#/components/schemas/AiLlmModel'
                        description: |
                          The LLM the job ran on. If your organization is over its AI credit downgrade threshold, this will be the organization's downgrade LLM.
                      provider:
                        $ref: '#/components/schemas/AiLlmProvider'
                        description: |
                          The LLM provider.
                      thinking:
                        $ref: '#/components/schemas/AiLlmThinkingLevels'
                        description: |
                          The thinking level, fixed when the job was submitted.
                    required:
                      - model
                      - provider
                    description: |
                      The LLM the job is pinned to, or `null` when if the job used the organization default. If your organization is over its AI credit downgrade threshold, this will be the organization's downgrade LLM.
                    example:
                      model: claude-sonnet-5
                      provider: bedrock
                      thinking: medium
                  omniChatUrl:
                    type: string
                    format: uri
                    description: |
                      URL to view this conversation in the Omni chat interface. Opens the chat session where the job actions and results are visible.

                      Omitted for MCP requests (`X-MCP-Request: true`) when the organization hides Omni links from MCP clients.
                    example: "https://blobsrus.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
                  progress:
                    type: object
                    nullable: true
                    description: |
                      **Only present in `EXECUTING` state.** Real-time progress information. Null if no progress has been reported yet. Updated in real-time as the AI works through iterations.
                    properties:
                      iteration:
                        type: integer
                        description: Current iteration number. The AI may take multiple iterations to refine queries and generate a complete answer.
                        example: 2
                      message:
                        type: string
                        description: |
                          Human-readable status message describing what the AI is currently doing.

                          While the agent is preparing its final answer (the last step of a job that ends on a `summarize` or `validate_analysis` action), this reads `"Preparing the answer"` instead of the final answer text, so a client polling for progress — or the progress webhook — doesn't see the answer before the job actually completes.
                        example: "Running query: Top products by revenue"
                      toolName:
                        type: string
                        description: |
                          The tool the AI is running in this step, using the same vocabulary as `actions[].type` on the [Stream AI job results](/api/ai/stream-ai-job-results) endpoint, such as `generate_query` or `search_model`. Absent for steps that aren't a tool call, such as starting a new iteration.
                        example: "generate_query"
                      updatedAt:
                        type: string
                        format: date-time
                        description: When this progress update was recorded.
                        example: "2025-01-15T10:00:08.000Z"
                    required:
                      - iteration
                      - message
                      - updatedAt
                  resultSummary:
                    type: string
                    description: |
                      **Only present in COMPLETE state.** Markdown-formatted summary of the job result. For the full result with query details and data, use the [Stream AI job results endpoint](/api/ai/stream-ai-job-results).
                    example: "### Top 5 Products by Revenue\n\n1. **Sunglasses** - $678,994\n2. **Jeans** - $475,072"
                  error:
                    type: object
                    description: |
                      **Only present in `FAILED` state.** Error details explaining why the job failed.
                    properties:
                      code:
                        type: string
                        description: Machine-readable error code.
                        example: "QUERY_EXECUTION_ERROR"
                      message:
                        type: string
                        description: Human-readable error message.
                        example: "Column 'revenue' not found in table 'orders'"
                      detail:
                        type: string
                        description: Additional error detail or context.
                        example: "The query timed out after 300 seconds"
                    required:
                      - message
                  createdAt:
                    type: string
                    format: date-time
                    description: When the job was submitted.
                    example: "2025-01-15T10:00:00.000Z"
                  executionStartedAt:
                    type: string
                    format: date-time
                    description: When execution began. Present once the job transitions from `QUEUED` to `EXECUTING`. May be absent on jobs that failed or were cancelled before execution started.
                    example: "2025-01-15T10:00:05.000Z"
                  completedAt:
                    type: string
                    format: date-time
                    description: |
                      **Present in `COMPLETE` and `FAILED` states.** When the job finished (successfully or with error).
                    example: "2025-01-15T10:01:30.000Z"
                  cancelledAt:
                    type: string
                    format: date-time
                    description: |
                       When the job was cancelled.
                    example: "2025-01-15T10:00:12.000Z"
                  cancelledBy:
                    type: string
                    format: uuid
                    description: |
                      **Only present in `CANCELLED` state.** User ID of who cancelled the job.
                    example: "990e8400-e29b-41d4-a716-446655440004"
                  updatedAt:
                    type: string
                    format: date-time
                    description: When the job record was last modified.
                    example: "2025-01-15T10:00:05.000Z"
                required:
                  - branchId
                  - conversationId
                  - createdAt
                  - id
                  - modelId
                  - organizationId
                  - prompt
                  - requestedLlm
                  - state
                  - topicName
                  - updatedAt
                  - userId
              examples:
                queued:
                  summary: QUEUED — job is waiting to be picked up
                  value:
                    id: "550e8400-e29b-41d4-a716-446655440000"
                    state: "QUEUED"
                    prompt: "What are the top 5 products by revenue?"
                    conversationId: "660e8400-e29b-41d4-a716-446655440001"
                    modelId: "770e8400-e29b-41d4-a716-446655440002"
                    branchId: null
                    organizationId: "880e8400-e29b-41d4-a716-446655440003"
                    userId: "990e8400-e29b-41d4-a716-446655440004"
                    topicName: null
                    requestedLlm: null
                    omniChatUrl: "https://my-org.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
                    createdAt: "2025-01-15T10:00:00.000Z"
                    updatedAt: "2025-01-15T10:00:00.000Z"
                executing:
                  summary: EXECUTING — AI is working through iterations, pinned to a specific LLM
                  value:
                    id: "550e8400-e29b-41d4-a716-446655440000"
                    state: "EXECUTING"
                    prompt: "What are the top 5 products by revenue?"
                    conversationId: "660e8400-e29b-41d4-a716-446655440001"
                    modelId: "770e8400-e29b-41d4-a716-446655440002"
                    branchId: null
                    organizationId: "880e8400-e29b-41d4-a716-446655440003"
                    userId: "990e8400-e29b-41d4-a716-446655440004"
                    topicName: "order_items"
                    requestedLlm:
                      model: "claude-sonnet-5"
                      provider: "bedrock"
                      thinking: "medium"
                    omniChatUrl: "https://my-org.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
                    progress:
                      iteration: 2
                      message: "Running query: Top products by revenue"
                      toolName: "generate_query"
                      updatedAt: "2025-01-15T10:00:08.000Z"
                    createdAt: "2025-01-15T10:00:00.000Z"
                    executionStartedAt: "2025-01-15T10:00:05.000Z"
                    updatedAt: "2025-01-15T10:00:08.000Z"
                complete:
                  summary: COMPLETE — job finished successfully
                  value:
                    id: "550e8400-e29b-41d4-a716-446655440000"
                    state: "COMPLETE"
                    prompt: "What are the top 5 products by revenue?"
                    conversationId: "660e8400-e29b-41d4-a716-446655440001"
                    modelId: "770e8400-e29b-41d4-a716-446655440002"
                    branchId: null
                    organizationId: "880e8400-e29b-41d4-a716-446655440003"
                    userId: "990e8400-e29b-41d4-a716-446655440004"
                    topicName: "order_items"
                    requestedLlm: null
                    omniChatUrl: "https://my-org.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
                    resultSummary: "### Top 5 Products by Revenue\n\n1. **Sunglasses** - $678,994\n2. **Jeans** - $475,072"
                    createdAt: "2025-01-15T10:00:00.000Z"
                    executionStartedAt: "2025-01-15T10:00:05.000Z"
                    completedAt: "2025-01-15T10:01:30.000Z"
                    updatedAt: "2025-01-15T10:01:30.000Z"
                failed:
                  summary: FAILED — job encountered an error
                  value:
                    id: "550e8400-e29b-41d4-a716-446655440000"
                    state: "FAILED"
                    prompt: "What are the top 5 products by revenue?"
                    conversationId: "660e8400-e29b-41d4-a716-446655440001"
                    modelId: "770e8400-e29b-41d4-a716-446655440002"
                    branchId: null
                    organizationId: "880e8400-e29b-41d4-a716-446655440003"
                    userId: "990e8400-e29b-41d4-a716-446655440004"
                    topicName: "order_items"
                    requestedLlm: null
                    omniChatUrl: "https://my-org.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
                    error:
                      code: "QUERY_EXECUTION_ERROR"
                      message: "Column 'revenue' not found in table 'orders'"
                      detail: "The query timed out after 300 seconds"
                    createdAt: "2025-01-15T10:00:00.000Z"
                    executionStartedAt: "2025-01-15T10:00:05.000Z"
                    completedAt: "2025-01-15T10:01:30.000Z"
                    updatedAt: "2025-01-15T10:01:30.000Z"
                cancelled:
                  summary: CANCELLED — job was cancelled by a user
                  value:
                    id: "550e8400-e29b-41d4-a716-446655440000"
                    state: "CANCELLED"
                    prompt: "What are the top 5 products by revenue?"
                    conversationId: "660e8400-e29b-41d4-a716-446655440001"
                    modelId: "770e8400-e29b-41d4-a716-446655440002"
                    branchId: null
                    organizationId: "880e8400-e29b-41d4-a716-446655440003"
                    userId: "990e8400-e29b-41d4-a716-446655440004"
                    topicName: null
                    requestedLlm: null
                    omniChatUrl: "https://my-org.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
                    createdAt: "2025-01-15T10:00:00.000Z"
                    executionStartedAt: "2025-01-15T10:00:05.000Z"
                    cancelledAt: "2025-01-15T10:00:12.000Z"
                    cancelledBy: "990e8400-e29b-41d4-a716-446655440004"
                    updatedAt: "2025-01-15T10:00:12.000Z"
        '400':
          description: Invalid job ID format. Must be a valid UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: 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.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Job not found. The job may not exist or may belong to a different organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/jobs/{jobId}/cancel:
    post:
      tags:
        - AI
      summary: Cancel AI job
      description: |
        Request cancellation of an AI job. This endpoint is idempotent — calling it on an already-cancelled or completed job returns success with the current state. 
        
        - For `QUEUED` jobs, cancellation is immediate
        - For `EXECUTING` jobs, the worker will stop after completing its current iteration
        - Jobs in a `DELIVERING` state cannot be cancelled as they are already finalizing results. **Only the job owner or organization admins can cancel jobs.**
      security:
        - bearerAuth: []
      operationId: cancelAIJob
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the AI job to cancel
      responses:
        '200':
          description: Cancellation request processed. The `state` field indicates the job's state after the attempt — `CANCELLED` if successful, or the current terminal state if the job had already completed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  jobId:
                    type: string
                    format: uuid
                    description: The unique identifier for the cancelled job
                  state:
                    type: string
                    enum: [CANCELLED, COMPLETE, DELIVERING, EXECUTING, FAILED, QUEUED]
                    description: The job's state after the cancellation attempt. `CANCELLED` if the cancellation succeeded. If the job was already in a terminal state (`COMPLETE`, `FAILED`, `CANCELLED`), the current state is returned unchanged.
              example:
                jobId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                state: "CANCELLED"
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Job is not in a cancellable state`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: |
            Unauthorized

            Possible error messages:
            - `Missing or invalid API key`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Forbidden

            Possible error messages:
            - `Permission denied. Only the job owner or organization admins can cancel jobs.`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Job not found. The job may not exist or may belong to a different organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '409':
          description: Concurrent modification conflict. The job's state changed between Omni reading it and recording the cancellation. Retry the request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          description: Omni could not record the cancellation, so the job was left in its current state. Retryable.
          content:
            application/json:
              schema:  # AiJobCancelApiError503
                type: object
                properties:
                  detail:
                    type: string
                    description: Human-readable error message describing what went wrong.
                    example: Could not record the cancellation, so the job was not cancelled. Retry the request.
                  status:
                    type: integer
                    description: HTTP status code of the error.
                    example: 503
                required:
                  - detail
                  - status

  /v1/ai/jobs/{jobId}/feedback:
    post:
      tags:
        - AI
      summary: Rate an AI job's response
      description: |
        Rate an AI job's response using the same thumbs up/thumbs down and optional comment a user can give a response in the Omni UI or in Slack. 
        
        The job must have finished or the API returns a `409`. Poll the [Get AI job status](/api/ai/get-ai-job-status) endpoint to retrieve a job's current status. Submit feedback once per job.

        <Note>
          Personal Access Tokens can only rate their own jobs. If using an Organization API key, pass `userId` to attribute the feedback to a specific user. This must be the same `userId` the job was submitted with.
        </Note>
      security:
        - bearerAuth: []
      operationId: aiJobFeedbackSubmit
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            description: The unique identifier of the AI job
            example: 123e4567-e89b-12d3-a456-426614174000
          description: The unique identifier of the AI job
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: true
        content:
          application/json:
            schema:  # AiJobFeedbackBody
              type: object
              properties:
                comment:
                  type: string
                  minLength: 1
                  maxLength: 5000
                  description: |
                    Free-text feedback about the response, such as what was wrong or what an automated evaluation found. Whitespace is trimmed; null is treated as no comment.
                  example: Grouped by order date instead of ship date.
                rating:
                  type: string
                  enum:
                    - good
                    - bad
                  description: |
                    The verdict on the response. `good` is a thumbs up, `bad` a thumbs down.
                  example: bad
              required:
                - rating
              additionalProperties: false
            example:
              rating: bad
              comment: Grouped by order date instead of ship date.
      responses:
        '200':
          description: Feedback recorded.
          content:
            application/json:
              schema:  # AiJobFeedbackResponse
                type: object
                properties:
                  comment:
                    type: string
                    description: The comment as recorded, or null when none was sent.
                    example: Grouped by order date instead of ship date.
                  conversationId:
                    type: string
                    format: uuid
                    description: The conversation the rated job belongs to. Analytics keys feedback by job id and conversation id.
                    example: 660e8400-e29b-41d4-a716-446655440001
                  jobId:
                    type: string
                    format: uuid
                    description: The job whose response was rated.
                    example: 550e8400-e29b-41d4-a716-446655440000
                  rating:
                    type: string
                    enum:
                      - good
                      - bad
                    description: The verdict on the response. `good` is a thumbs up, `bad` a thumbs down.
                    example: bad
                  submittedAt:
                    type: string
                    format: date-time
                    description: When the feedback was recorded.
                    example: '2025-01-15T10:00:00.000Z'
                required:
                  - comment
                  - conversationId
                  - jobId
                  - rating
                  - submittedAt
              example:
                jobId: "550e8400-e29b-41d4-a716-446655440000"
                conversationId: "660e8400-e29b-41d4-a716-446655440001"
                rating: "bad"
                comment: "Grouped by order date instead of ship date."
                submittedAt: "2025-01-15T10:00:00.000Z"
        '400':
          description: Invalid job ID (must be a UUID) or request body — unknown rating, unrecognized field, or a comment that is empty or over 5000 characters.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Permission denied. AI query generation is disabled for the organization, the caller lacks AI access on the job's model, or a user-scoped key passed a different user's `userId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Job not found. The job may not exist, may belong to a different organization, or — for user-scoped keys — may belong to a different user. `userId` not found in the organization also returns 404.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '409':
          description: |
            The job is not `COMPLETE` or `FAILED`. For a `QUEUED`, `EXECUTING`, or `DELIVERING` job, poll the [Get AI job status](/api/ai/get-ai-job-status) endpoint and retry once it finishes. A `CANCELLED` job can never be rated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/jobs/{jobId}/result:
    get:
      tags:
        - AI
      summary: Stream AI job results
      description: |
        Retrieve the full result of a completed AI job, including all actions taken by the AI (queries generated, data retrieved) and the final summarized answer. 
        
        Results are only available for jobs in `COMPLETE` state and are retained for 14 days after completion. The response is streamed directly from storage.
      security:
        - bearerAuth: []
      operationId: getAIJobResult
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the AI job
      responses:
        '200':
          description: Job results retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  actions:
                    type: array
                    description: Ordered list of actions the AI took during execution. Each action represents a step such as generating a query, executing it, or synthesizing a final answer.
                    items:
                      type: object
                      required:
                        - message
                        - timestamp
                        - type
                      properties:
                        type:
                          type: string
                          description: |
                            The type of action. Common types include:

                            - `generate_query` - Query generation and execution
                            - `summarize` - Final answer synthesis
                            - `validate_analysis` - Post-answer check for critical errors in the analysis
                            - `publish_dashboard` - Publishing a dashboard draft to make changes live

                            When validation is enabled for the organization, the final answer can be on a `validate_analysis` action instead of `summarize`. Read the top-level `message` or `resultSummary` fields for the final answer rather than assuming it's on the last action.
                          example: "generate_query"
                        status:
                          type: string
                          enum: [pending, complete, partial, skipped, failed]
                          description: |
                            The outcome of this action:

                            - `pending` - The action is still in progress, such as a query the AI is still running.
                            - `complete` - The action finished successfully.
                            - `partial` - The action finished with a caveat, such as a note about incomplete fulfillment.
                            - `skipped` - The action didn't result in a change, such as navigating to a tab that was already open.
                            - `failed` - The action failed with an error.
                          example: "complete"
                        message:
                          type: string
                          description: The AI's explanation of what it is doing in this step, written in natural language.
                          example: "I'll generate a query to find the top 5 products by total revenue."
                        timestamp:
                          type: string
                          description: ISO 8601 timestamp when this action occurred.
                          example: "2025-01-15T10:00:10.000Z"
                        result:
                          type: object
                          description: Query result data. Only present for `generate_query` action types.
                          required:
                            - csvResult
                            - csvResultWasTruncated
                            - hasResults
                            - query
                            - queryName
                            - status
                            - totalRowCount
                          properties:
                            resultId:
                              type: string
                              description: Stable, unique identifier for this query result within the job. Use it to reference a specific result — for example, to correlate or de-duplicate results across responses.
                              example: "928c5838-000d-4943-b305-f6242c1b4922"
                            queryName:
                              type: string
                              description: Human-readable name describing what this query retrieves.
                              example: "Top 5 Products by Revenue"
                            status:
                              type: string
                              enum: [success, error]
                              description: Whether the query executed successfully.
                              example: "success"
                            query:
                              type: object
                              description: The semantic query definition that was executed. This can be used with the [Run query endpoint](/api/queries/run-query) to re-run the query.
                            csvResult:
                              type: string
                              description: Query results formatted as CSV text.
                              example: "Name,Total Revenue\nRay-Ban Sunglasses,\"678,994.41\"\nLevi's 501 Jeans,\"475,072.00\""
                            csvResultWasTruncated:
                              type: boolean
                              description: Whether the CSV data was truncated due to size limits. If `true`, the full result set may contain additional rows not included in `csvResult`.
                              example: false
                            csvResultCellsWereTruncated:
                              type: boolean
                              description: Whether individual CSV cell values were cut short due to size limits. Truncated values end with `...`. A narrower query (fewer rows or fields) retrieves them in full.
                              example: false
                            hasResults:
                              type: boolean
                              description: Whether the query returned any data rows.
                              example: true
                            totalRowCount:
                              type: integer
                              description: Total number of rows returned by the query.
                              example: 5
                  message:
                    type: string
                    description: |
                      The AI's final response message in Markdown format. This is the complete answer to the original prompt, incorporating data from all executed queries.

                      When the AI publishes a dashboard, the message discloses that the publish occurred. If the published draft contained changes made by someone other than the caller (drafts are per-dashboard, not per-user), the message also discloses that fact with attribution and timestamp.
                    example: "### Top 5 Products by Revenue\n\n1. **Sunglasses** - $678,994\n2. **Jeans** - $475,072"
                  resultSummary:
                    type: string
                    description: |
                      Summary of the job result. Typically matches the final message content.

                      When the AI publishes a dashboard, the summary discloses that the publish occurred. If the published draft contained changes made by someone other than the caller, the summary also discloses that fact.
                    example: "### Top 5 Products by Revenue\n\n1. **Sunglasses** - $678,994\n2. **Jeans** - $475,072"
                  topic:
                    type: string
                    description: The topic name used for query generation.
                    example: "order_items"
                  omniChatUrl:
                    type: string
                    format: uri
                    description: URL to view this conversation in the Omni chat interface. Opens the chat session where the job actions and results are visible.
                    example: "https://my-org.omni.co/chat/660e8400-e29b-41d4-a716-446655440001"
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Invalid job ID`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: |
            Unauthorized

            Possible error messages:
            - `Missing or invalid API key`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Job not found or results not available

            Possible error messages:
            - `Job not found`
            - `Job results not yet available`
            - `Result is no longer available`. Results are retained for 14 days.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/jobs/{jobId}/vis:
    get:
      tags:
        - AI
      summary: Render AI job visualization
      description: |
        Render the visualization from a completed AI job as a PNG image.
      security:
        - bearerAuth: []
      operationId: aiJobVisualization
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the AI job
        - name: auto_vis
          in: query
          schema:
            type: string
            default: "true"
          description: |
            The literal string `false` disables automatic chart selection and returns a `204` if the job has no visualization. Any other string, including an empty string and `0`, enables automatic selection. Defaults to `true`.
        - name: debug
          in: query
          schema:
            type: string
            default: "false"
          description: |
            Returns debug JSON instead of the rendered PNG when enabled. The strings `false`, `0`, and the empty string disable debug; any other non-empty string, including `true` and `1`, enables it. Defaults to `false`.
      responses:
        '200':
          description: Visualization rendered as `image/png`, or `application/json` when debug is enabled or when the underlying data is unavailable. Debug JSON contains the compiled visualization and query details, or diagnostic information when the data is unavailable.
          content:
            image/png:
              schema:
                type: string
                format: binary
            application/json:
              schema:  # AiJobVisDebugResponse
                anyOf:
                  - type: object
                    properties:
                      compiledVegaLiteSpec:
                        type: object
                        additionalProperties:
                          $ref: '#/components/schemas/AiJobVisNonNullJsonValue'
                        description: Compiled Vega specification, or `null` if compilation produced no specification.
                      dataSource:
                        type: string
                        enum:
                          - arrow
                      hasQuery:
                        type: boolean
                        enum:
                          - true
                      hasResultFields:
                        type: boolean
                        enum:
                          - true
                      query:
                        $ref: '#/components/schemas/OmniQuery'
                      resultFields:
                        type: array
                        items:
                          type: object
                          properties:
                            label:
                              type: string
                            name:
                              type: string
                            type:
                              type: object
                              properties:
                                typeId:
                                  type: integer
                              required:
                                - typeId
                              additionalProperties:
                                $ref: '#/components/schemas/AiJobVisNonNullJsonValue'
                              description: Serialized Apache Arrow data type, when available.
                          required:
                            - label
                            - name
                      rowCount:
                        type: integer
                        minimum: 0
                      visConfig:
                        type: object
                        properties:
                          chartType:
                            type: string
                            enum:
                              - 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:
                            type: object
                            additionalProperties:
                              $ref: '#/components/schemas/AiJobVisNonNullJsonValue'
                    required:
                      - compiledVegaLiteSpec
                      - dataSource
                      - hasQuery
                      - hasResultFields
                      - query
                      - resultFields
                      - rowCount
                      - visConfig
                  - type: object
                    properties:
                      error:
                        type: string
                        description: Human-readable error message describing what went wrong.
                        example: No Arrow IPC data available for visualization
                      hasQuery:
                        type: boolean
                        enum:
                          - false
                      jobId:
                        type: string
                        format: uuid
                    required:
                      - error
                      - hasQuery
                      - jobId
        '204':
          description: No visualization exists, and `auto_vis=false` disabled automatic chart selection. The response has no body.
        '400':
          description: Invalid job ID format. Must be a valid UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: |
            Unauthorized

            Possible error messages:
            - `Missing or invalid API key`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: 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.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Job not found, not in `COMPLETE` state, or this endpoint isn't enabled for the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '422':
          description: |
            The job completed but can't be rendered as a visualization. Common causes: no visualization action in the job result, no underlying data available, missing summary fields, or the chart type isn't renderable as an image (for example, tables or KPIs).
          content:
            application/json:
              schema:  # ApiError422
                type: object
                properties:
                  error:
                    type: string
                    description: Human-readable error message describing what went wrong.
                    example: No Arrow IPC data available for visualization
                required:
                  - error
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/llms:
    get:
      tags:
        - AI
      summary: List available LLMs
      description: |
        List the LLMs the caller may pass as `requestedLlm` on the [Create AI job](/api/ai/create-ai-job) and [Generate a query](/api/ai/generate-a-query) endpoints.

        The response includes the organization's [approved LLMs](/ai/settings/llm-settings), [test-only](/ai/settings/llm-settings#test-only) LLMs only for organization admins, only the default for an embedded user, and only the [downgrade LLM](/ai/settings/usage#downgrade) while the organization is over its AI credit downgrade threshold.
      operationId: aiLlmsList
      parameters:
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: Available LLMs retrieved successfully.
          content:
            application/json:
              schema:  # AiLlmsListResponse
                type: object
                required:
                  - llms
                properties:
                  llms:
                    type: array
                    description: |
                      The LLMs the caller may pass.
                    items:  # AiPickableLlm
                      type: object
                      required:
                        - description
                        - isDefault
                        - label
                        - model
                        - provider
                        - thinkingLevels
                      properties:
                        label:
                          type: string
                          description: Display name, as it displays in the chat picker
                          example: Claude Sonnet 5.5
                        description:
                          $ref: '#/components/schemas/AiLlmDescription'
                          description: |
                            The LLM's description.
                        model:
                          $ref: '#/components/schemas/AiLlmModel'
                          description: |
                            The LLM. Use this value when passing `requestedLlm.model`.
                        provider:
                          $ref: '#/components/schemas/AiLlmProvider'
                          description: |
                            The LLM provider. Use this value when passing `requestedLlm.provider`.
                        thinkingLevels:
                          $ref: '#/components/schemas/AiLlmThinkingLevels'
                          description: |
                            The thinking levels this LLM supports, any of which may be passed as `requestedLlm.thinking`.
                        isDefault:
                          $ref: '#/components/schemas/AiLlmIsDefault'
                          description: |
                            Whether this is the LLM a job or query runs on when it doesn't name a `requestedLlm`.
              example:
                llms:
                  - label: Claude Sonnet 5.5
                    description: Fast answers for quick lookups
                    model: claude-sonnet-5-5
                    provider: bedrock
                    thinkingLevels:
                      - default
                      - low
                      - medium
                      - high
                    isDefault: true
                  - label: Claude Opus 5.5
                    description: Deeper analysis for complex questions
                    model: claude-opus-5-5
                    provider: bedrock
                    thinkingLevels:
                      - default
                      - medium
                      - high
                    isDefault: false
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: AI query generation is not enabled for the organization, the user cannot use AI, or a Personal Access Token passed another user's `userId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: The `userId` does not name a user in this organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/eval/prompt-sets:
    get:
      description: List eval prompt sets, sorted alphabetically by name. When `model_ids` is omitted, returns prompt sets for every shared model the caller can access. Requires at least the Querier role on each requested model.
      operationId: aiEvalPromptSetsList
      summary: List eval prompt sets
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            enum:
              - 'true'
              - 'false'
            description: When `true`, returns archived prompt sets instead of active ones. Defaults to `false`.
            example: 'false'
          required: false
          description: When `true`, returns archived prompt sets instead of active ones. Defaults to `false`.
          name: archived
          in: query
        - schema:
            type: array
            items:
              type: string
              format: uuid
            description: Optional list of model IDs to filter prompt sets by. When omitted, returns prompt sets for every model the caller can access. Supply multiple times to filter by more than one model (e.g., `?model_ids=A&model_ids=B`).
          required: false
          description: Optional list of model IDs to filter prompt sets by. When omitted, returns prompt sets for every model the caller can access. Supply multiple times to filter by more than one model (e.g., `?model_ids=A&model_ids=B`).
          name: model_ids
          in: query
      responses:
        '200':
          description: List of prompt sets, sorted alphabetically by name.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalPromptSetsListResponse'
        '400':
          description: Invalid query params (e.g. `model_ids` contains a non-UUID, or `archived` is not `true`/`false`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: &id001
                detail: 'Bad Request: name: Required'
                status: 400
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: &id002
                detail: 'Unauthorized: Missing or invalid API key'
                status: 401
        '403':
          description: Insufficient permissions. The caller must have at least the Querier role on each requested model.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: &id003
                detail: AI eval requires at least Querier access on the model
                status: 403
        '404':
          description: No eval-accessible models for this caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: &id004
                detail: Prompt set not found
                status: 404
    post:
      description: Create a new eval prompt set bound to a shared model. Initial prompts can be supplied; additional prompts can be added later via PATCH.
      operationId: aiEvalPromptSetsCreate
      summary: Create an eval prompt set
      tags:
        - AI Eval
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvalPromptSetsCreateBody'
      responses:
        '201':
          description: Prompt set created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalPromptSetsCreateResponse'
        '400':
          description: Invalid request body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id001
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions. The caller must have at least the Querier role on the model.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
  /v1/ai/eval/prompt-sets/{promptSetId}:
    get:
      description: Get a single prompt set with all of its prompts.
      operationId: aiEvalPromptSetsGet
      summary: Get an eval prompt set
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            format: uuid
            description: The unique identifier of the eval prompt set.
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          description: The unique identifier of the eval prompt set.
          name: promptSetId
          in: path
      responses:
        '200':
          description: Prompt set details.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalPromptSetsGetResponse'
        '400':
          description: Invalid `promptSetId` — must be a UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id001
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: Prompt set not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
    patch:
      description: Update a prompt set's name, description, and/or prompts. When `prompts` is supplied, it fully replaces the existing list — existing prompts omitted from the list are deleted, entries without an `id` are created, and entries with a matching `id` are updated in place.
      operationId: aiEvalPromptSetsUpdate
      summary: Update an eval prompt set
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            format: uuid
            description: The unique identifier of the eval prompt set.
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          description: The unique identifier of the eval prompt set.
          name: promptSetId
          in: path
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvalPromptSetsUpdateBody'
      responses:
        '200':
          description: Prompt set updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalPromptSetsUpdateResponse'
        '400':
          description: Invalid request body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id001
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: Prompt set not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
        '422':
          description: A `prompts[].id` in the request does not belong to this prompt set.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: &id005
                detail: A prompt being updated does not belong to this prompt set
                status: 422
    delete:
      description: 'Archive (soft-delete) a prompt set. Omni also attempts to cancel every in-flight agentic job associated with the set; the returned `cancelled_job_count` reports how many were cancelled. Cancellation is best-effort — the archive is committed first, so if a run-cancellation later fails the endpoint returns 500 even though the set is already archived. The call is idempotent: retrying drains any remaining runs.'
      operationId: aiEvalPromptSetsArchive
      summary: Archive an eval prompt set
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            format: uuid
            description: The unique identifier of the eval prompt set.
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          description: The unique identifier of the eval prompt set.
          name: promptSetId
          in: path
      responses:
        '200':
          description: Prompt set archived successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalPromptSetsDeleteResponse'
        '400':
          description: Invalid `promptSetId` — must be a UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id001
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: Prompt set not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
        '500':
          description: Archive committed but a run-cancellation failed; the set is already archived — safe to retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example:
                detail: Archive committed but a run-cancellation failed; retry to complete
                status: 500
  /v1/ai/eval/prompt-sets/{promptSetId}/unarchive:
    post:
      description: Restore an archived prompt set.
      operationId: aiEvalPromptSetsUnarchive
      summary: Restore an archived eval prompt set
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            format: uuid
            description: The unique identifier of the eval prompt set.
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          description: The unique identifier of the eval prompt set.
          name: promptSetId
          in: path
      responses:
        '200':
          description: Prompt set restored successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalPromptSetsUnarchiveResponse'
        '400':
          description: Invalid `promptSetId` — must be a UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id001
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: Prompt set not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
  /v1/ai/eval/runs:
    get:
      description: List runs for a prompt set, newest first, filtered to runs whose model the caller can access. The `prompt_set_id` query parameter is required.
      operationId: aiEvalRunsList
      summary: List eval runs
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            enum:
              - 'true'
              - 'false'
            description: When `true`, returns archived runs instead of active ones. Defaults to `false`.
            example: 'false'
          required: false
          description: When `true`, returns archived runs instead of active ones. Defaults to `false`.
          name: archived
          in: query
        - schema:
            type: string
            format: uuid
            description: Required — the prompt set whose runs should be listed.
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          description: Required — the prompt set whose runs should be listed.
          name: prompt_set_id
          in: query
      responses:
        '200':
          description: List of runs for the prompt set.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalRunsListResponse'
        '400':
          description: Missing or invalid `prompt_set_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id001
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: Prompt set not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
    post:
      description: Create and start a new run against an existing prompt set. The run enqueues one agentic job per prompt and begins executing immediately. Returns the newly created run with its initial per-prompt result rows.
      operationId: aiEvalRunsCreate
      summary: Start an eval run
      tags:
        - AI Eval
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvalRunsCreateBody'
      responses:
        '201':
          description: Run created and jobs enqueued.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalRunsCreateResponse'
        '400':
          description: Invalid request body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id001
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions. The caller must have at least the Querier role on the prompt set's model.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: The prompt set was not found, or `run_config.branch_id` does not match an existing branch in the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
        '422':
          description: '`run_config.branch_id` does not belong to the prompt set''s model, or `run_config.llm` is not an LLM the organization has approved, or names a thinking level that LLM doesn''t honor.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example:
                detail: 'claude-opus-5-5 on bedrock is not an approved LLM for this organization. Approved LLMs: claude-sonnet-5 (bedrock), gpt-6-sol (bedrock).'
                status: 422
        '429':
          description: Per-user active-run cap reached.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example:
                detail: Too many active runs; wait for an in-flight run to finish
                status: 429
        '503':
          description: AI eval is paused for this organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example:
                detail: AI eval is paused for this organization
                status: 503
  /v1/ai/eval/runs/{runId}:
    get:
      description: Get an eval run with every per-prompt result row, including the underlying agentic job state and any scoring data.
      operationId: aiEvalRunsGet
      summary: Get an eval run
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            format: uuid
            description: The unique identifier of the eval run.
            example: 660e8400-e29b-41d4-a716-446655440001
          required: true
          description: The unique identifier of the eval run.
          name: runId
          in: path
      responses:
        '200':
          description: Run detail.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalRunsGetResponse'
        '400': &id006
          description: Invalid `runId` — must be a UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example:
                detail: 'Bad Request: runId: Invalid uuid'
                status: 400
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: Run not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
    delete:
      description: Archive (soft-delete) an eval run. Any non-terminal per-prompt agentic jobs are cancelled as part of the archive (best-effort), and a still-RUNNING run is flipped to CANCELLED before archival. The call is idempotent; archiving an already-terminal or already-archived run is a no-op.
      operationId: aiEvalRunsArchive
      summary: Archive an eval run
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            format: uuid
            description: The unique identifier of the eval run.
            example: 660e8400-e29b-41d4-a716-446655440001
          required: true
          description: The unique identifier of the eval run.
          name: runId
          in: path
      responses:
        '200':
          description: Run archived successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalRunsDeleteResponse'
        '400': *id006
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: Run not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
  /v1/ai/eval/runs/{runId}/cancel:
    post:
      description: 'Cancel an in-flight eval run. Marks the run cancelled first so no in-progress per-prompt job can flip it back to COMPLETE, then cancels every non-terminal agentic job associated with the run. The run is also archived as part of the cancel — the response returns the updated run inline (`status: CANCELLED`, `is_archived: true`); use `/unarchive` to surface it in the default `archived=false` list again.'
      operationId: aiEvalRunsCancel
      summary: Cancel an eval run
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            format: uuid
            description: The unique identifier of the eval run.
            example: 660e8400-e29b-41d4-a716-446655440001
          required: true
          description: The unique identifier of the eval run.
          name: runId
          in: path
      responses:
        '200':
          description: Cancellation processed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalRunsCancelResponse'
        '400': *id006
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: Run not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
  /v1/ai/eval/runs/{runId}/unarchive:
    post:
      description: Restore an archived eval run.
      operationId: aiEvalRunsUnarchive
      summary: Restore an archived eval run
      tags:
        - AI Eval
      parameters:
        - schema:
            type: string
            format: uuid
            description: The unique identifier of the eval run.
            example: 660e8400-e29b-41d4-a716-446655440001
          required: true
          description: The unique identifier of the eval run.
          name: runId
          in: path
      responses:
        '200':
          description: Run restored successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalRunsUnarchiveResponse'
        '400': *id006
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id002
        '403':
          description: Insufficient permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id003
        '404':
          description: Run not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvalApiError'
              example: *id004
  /v1/models/{modelId}/suggestions:
    get:
      tags:
        - AI Model Suggestions
      summary: List AI-generated suggestions
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Lists AI-generated suggestions for a shared model. Suggestions can be filtered by status and are returned with keyset-based pagination.

        Suggestions are sorted by priority, creation date, and ID for stable pagination.
      security:
        - bearerAuth: []
      operationId: listModelSuggestions
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the shared model to list suggestions for.
        - name: status
          in: query
          schema:
            type: string
            enum:
              - active
              - ignored
              - all
            default: active
          description: |
            Filter suggestions by status:

            - `active` - Only active suggestions (default)
            - `ignored` - Only ignored suggestions
            - `all` - All suggestions regardless of status
        - name: cursor
          in: query
          schema:
            type: string
          description: Cursor for keyset-based pagination. Use the `id` from the last suggestion in the previous page.
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
          description: Number of suggestions to return per page. Defaults to 25, maximum 100.
      responses:
        '200':
          description: List of suggestions for the model
          content:
            application/json:
              schema:
                type: object
                required:
                  - records
                  - pageInfo
                properties:
                  records:
                    type: array
                    description: List of AI-generated suggestions
                    items:
                      $ref: '#/components/schemas/ModelSuggestion'
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
              example:
                records:
                  - id: "3fa85f64-5717-4562-b3fc-2c963f66afa6"
                    category: "missing_context"
                    title: "Document the orders view"
                    rationale: "Users frequently ask what `status` means; documenting the view in ai_context improves AI answers."
                    priority: 1
                    proposedChanges:
                      kind: "context_edits"
                      edits:
                        - field: "ai_context"
                          target: "views.orders"
                          value: "The orders view contains one row per customer order."
                    evidence:
                      - type: "ai_chat"
                        chatAiSessionId: "a1b2c3d4-5717-4562-b3fc-2c963f66afa6"
                        capturedAt: "2026-06-24T09:12:33Z"
                    ignoreReason: null
                    ignoredAt: null
                    ignoredBy: null
                    aiModifiedAt: "2026-06-25T16:10:58Z"
                    createdAt: "2026-06-25T16:10:58Z"
                    updatedAt: "2026-06-25T16:10:58Z"
                  - id: "b2c3d4e5-6717-4562-b3fc-2c963f66afa6"
                    category: "missing_context"
                    title: "Clarify the revenue measure"
                    rationale: "The revenue measure lacks a description, leading to ambiguous AI responses."
                    priority: 3
                    proposedChanges:
                      kind: "context_edits"
                      edits:
                        - field: "ai_context"
                          target: "views.orders.fields.revenue"
                          value: "Revenue is recognized at order completion, net of refunds."
                    evidence: []
                    ignoreReason: null
                    ignoredAt: null
                    ignoredBy: null
                    aiModifiedAt: "2026-06-24T11:05:00Z"
                    createdAt: "2026-06-24T11:05:00Z"
                    updatedAt: "2026-06-24T11:05:00Z"
                pageInfo:
                  hasNextPage: true
                  nextCursor: "b2c3d4e5-6717-4562-b3fc-2c963f66afa6"
                  pageSize: 25
                  totalRecords: 47
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Invalid cursor value`
            - `modelId: Invalid UUID`
            - `pageSize must be between 1 and 100`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Bad Request: Invalid cursor value"
                status: 400
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden. Possible causes:

            - Requesting user lacks Organization Admin permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Forbidden: Organization admin permission required"
                status: 403
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Shared model with id <modelId> does not exist`
            - `Model not found or not accessible`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Shared model with id 550e8400-e29b-41d4-a716-446655440000 does not exist"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
  
  /v1/models/{modelId}/suggestions/{suggestionId}:
    delete:
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Permanently deletes an AI Model suggestion.
      operationId: modelSuggestionsDelete
      summary: Delete a suggestion
      tags:
        - AI Model Suggestions
      parameters:
        - name: modelId
          in: path
          schema:
            type: string
            format: uuid
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
          required: true
          description: UUID of the shared model the suggestion belongs to
          
        - name: suggestionId
          in: path
          schema:
            type: string
            format: uuid
            example: b2c3d4e5-f6a7-8901-bcde-f12345678901
          required: true
          description: UUID of the suggestion
      responses:
        '200':
          description: The suggestion was deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Malformed ID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: AI disabled for organization, model is not a shared model, or caller lacks Organization Admin permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Suggestion or model not found in this organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/models/{modelId}/suggestions/runs/latest:
    get:
      summary: Get the latest generation run
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Retrieve the most recent generation run for the shared model (the active run if one is in flight, otherwise the last terminal run), or `null` if none exists.
      operationId: modelSuggestionsRunLatest
      security:
        - bearerAuth: []
      tags:
        - AI Model Suggestions
      parameters:
        - name: modelId
          in: path
          required: true
          description: UUID of the shared model the suggestions belong to
          schema:
            type: string
            format: uuid
            description: UUID of the shared model the suggestions belong to
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
      responses:
        '200':
          description: The latest generation run, or null
          content:
            application/json:
              schema:
                type: object
                description: The most recent generation run or `null` if no runs exist.
                required:
                  - run
                properties:
                  run:
                    allOf:
                      - $ref: '#/components/schemas/SuggestionRun'
                      - type: object
                        nullable: true
                        description: The most recent run for this model or `null` if none exists.
              examples:
                withRun:
                  summary: Model has runs
                  value:
                    run:
                      id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                      status: "complete"
                      triggerSource: "manual"
                      triggeredBy:
                        userId: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                        name: "Blob Ross"
                      createdAt: "2026-07-15T14:30:00Z"
                      executionStartedAt: "2026-07-15T14:30:05Z"
                      completedAt: "2026-07-15T14:32:30Z"
                      error: null
                noRuns:
                  summary: Model has never had a run
                  value:
                    run: null
        '400':
          description: Malformed `modelId`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: AI disabled, model is not a shared model, or caller lacks Organization Admin permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Model not found in this organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/models/{modelId}/suggestions/runs/{runId}:
    get:
      summary: Get status for a generation run
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Retrieve the status of a specific generation run. Poll until `status` is terminal (`complete` or `failed`), then re-fetch the suggestions list.
      operationId: modelSuggestionsRunGet
      security:
        - bearerAuth: []
      tags:
        - AI Model Suggestions
      parameters:
        - name: modelId
          in: path
          required: true
          description: UUID of the shared model the run belongs to
          schema:
            type: string
            format: uuid
            description: UUID of the shared model the run belongs to
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        - name: runId
          in: path
          required: true
          description: UUID of the generation run
          schema:
            type: string
            format: uuid
            description: UUID of the generation run
            example: b2c3d4e5-f6a7-8901-bcde-f12345678901
      responses:
        '200':
          description: The generation run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuggestionRun'
              examples:
                completed:
                  summary: Completed run
                  value:
                    id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    status: "complete"
                    triggerSource: "manual"
                    triggeredBy:
                      userId: "c3d4e5f6-a7b8-9012-cdef-123456789012"
                      name: "Blob Ross"
                    createdAt: "2026-07-15T14:30:00Z"
                    executionStartedAt: "2026-07-15T14:30:05Z"
                    completedAt: "2026-07-15T14:32:30Z"
                    error: null
                running:
                  summary: Run in progress
                  value:
                    id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    status: "running"
                    triggerSource: "scheduled"
                    triggeredBy:
                      userId: "c3d4e5f6-a7b8-9012-cdef-123456789012"
                      name: "Blob Ross"
                    createdAt: "2026-07-15T14:30:00Z"
                    executionStartedAt: "2026-07-15T14:30:05Z"
                    completedAt: null
                    error: null
        '400':
          description: Malformed `modelId` or `runId`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: AI disabled, model is not a shared model, or caller lacks Organization Admin permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Run not found, does not belong to this model, or does not belong to this organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/routines:
    get:
      summary: List AI Routines
      description: |
        List AI Routines for the calling user, newest first. Includes routines paused by the owner or disabled by Omni, but excludes deleted routines. 
        
        Use `pageInfo.nextCursor` from one response as the `cursor` query parameter on the next request.
      operationId: routinesList
      tags:
        - AI Routines
      parameters:
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/pageSize'
        - $ref: '#/components/parameters/sortDirection'
        - name: sortField
          in: query
          schema:
            type: string
          required: false
          description: Field to sort results by
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: List of AI Routines
          content:
            application/json:
              schema:
                type: object
                required:
                  - pageInfo
                  - records
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    items:
                      $ref: '#/components/schemas/Routine'
                    description: |
                      Routines returned for this request, newest first.

                      Use the [Get an AI routine endpoint](/api/ai-routines/get-an-ai-routine) to retrieve queries for conditional routines.
        '400':
          description: Invalid pagination, sorting, `userId`, or unknown query parameter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            AI routines or AI query generation are not enabled for the organization, the caller lacks AI access, or a user-scoped API key tried to list routines for another user.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not found.
            
            Possible causes:
            
            - The `userId` membership was not found in the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      summary: Create an AI Routine
      description: |
        Create a new AI Routine that runs a saved prompt on a schedule and delivers the AI response through email or Slack. To run a routine when a condition is met, provide a `condition` in the request body.

        Each scheduled run executes once using the routine owner's permissions, and every recipient receives the same result.
      operationId: routineCreate
      tags:
        - AI Routines
      parameters:
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - modelId
                - prompt
                - schedule
                - timezone
                - destination
              properties:
                branchId:
                  type: string
                  nullable: true
                  format: uuid
                  description: Branch of the shared model the prompt runs against, or null.
                description:
                  type: string
                  maxLength: 2000
                  description: Optional human-readable notes about the routine. Display-only — never used as model input.
                  example: Weekly signups summary for the growth team.
                modelId:
                  type: string
                  format: uuid
                  description: The UUID of the model the prompt runs against. Must be a shared model, or a shared-extension model usable as a workbook base.
                  example: 770e8400-e29b-41d4-a716-446655440002
                name:
                  type: string
                  minLength: 1
                  maxLength: 512
                  description: Customer-visible name of the routine. Used as the email subject for email destinations, and shown on Slack deliveries.
                  example: Weekly user signups
                prompt:
                  type: string
                  minLength: 1
                  description: Natural language prompt Omni runs on each scheduled run. Run a skill as part of the routine by including a skill's `/identifier`, which will resolve as the routine owner when the routine fires.
                  example: How many users signed up last week?
                schedule:
                  type: string
                  minLength: 1
                  description: |
                    Six-field cron expression (minute, hour, day-of-month, month, day-of-week, year; use `?` for an unspecified day field). 
                  
                    Minimum frequency is once per hour; contact Omni support if you need more frequent scheduling.
                  example: 0 9 ? * MON *
                timezone:
                  type: string
                  description: IANA timezone identifier used to evaluate the schedule.
                  example: America/New_York
                topicName:
                  type: string
                  maxLength: 256
                  description: Topic name to scope query generation. If omitted, the AI picks the best topic.
                  example: users
                destination:
                  $ref: '#/components/schemas/RoutineDestination'
                condition:
                  type: object
                  required:
                    - conditionPrompt
                  description: |
                    Delivers only when this condition is met. Omni authors and verifies a semantic query from the condition text before creating the routine. 
                    
                    Omit for a routine that delivers on every scheduled run.
                  properties:
                    conditionPrompt:
                      type: string
                      minLength: 1
                      maxLength: 2000
                      description: The delivery condition, in plain language. Omni authors and verifies a semantic query from this text.
                      example: there are failed orders
                  additionalProperties: false
            examples:
              scheduledRoutine:
                summary: Create a scheduled routine
                value:
                  name: Weekly user signups
                  modelId: 770e8400-e29b-41d4-a716-446655440002
                  prompt: How many users signed up last week?
                  schedule: 0 9 ? * MON *
                  timezone: America/New_York
                  destination:
                    type: email
                    recipientEmails:
                      - blob.ross@blobsrus.com
                      - blob.the.builder@blobsrus.com
              conditionalRoutine:
                summary: Create a conditional routine
                value:
                  name: Weekly user signups
                  modelId: 770e8400-e29b-41d4-a716-446655440002
                  prompt: How many users signed up last week?
                  schedule: 0 9 ? * MON *
                  timezone: America/New_York
                  condition:
                    conditionPrompt: Run when more than 1000 users sign up in a single week
                  destination:
                    type: email
                    recipientEmails:
                      - blob.ross@blobsrus.com
                      - blob.the.builder@blobsrus.com
      
      responses:
        '201':
          description: Routine created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  condition:
                    description: The generated condition, present only when the request supplied one.
                    allOf:
                      - $ref: '#/components/schemas/RoutineCondition'
                      - type: object
                        properties:
                          interpretation:
                            type: string
                            description: A plain-language explanation of how the authored query implements the requested condition.
                          query:
                            $ref: '#/components/schemas/RoutineConditionQuery'
                          verification:
                            $ref: '#/components/schemas/RoutineConditionVerification'
                        required:
                          - interpretation
                          - query
                          - verification
                  id:
                    type: string
                    format: uuid
                    description: The unique identifier for the newly created routine.
                    example: 880e8400-e29b-41d4-a716-446655440003
                required:
                  - id
        '400':
          $ref: '#/components/responses/RoutineConditionApiError400'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '402':
          $ref: '#/components/responses/AiCreditShutoffError'
        '403':
          description: |
            Forbidden

            AI routines or AI query generation are not enabled for the organization, or the API key cannot act on behalf of the requested user.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Model, branch, or topic not found, or not accessible to the requested user. Also returned when the `userId` membership has not accepted its invitation to the organization yet.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          description: The resolved user already has the maximum number of active routines.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError429'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/RoutineConditionApiError503'

  /v1/ai/routines/{routineId}:
    get:
      summary: Get an AI Routine
      description: |
        Retrieve details for a specific AI Routine by ID, including its persisted condition query and the status of its most recent completed run.
      operationId: routineGet
      tags:
        - AI Routines
      parameters:
        - name: routineId
          in: path
          schema:
            type: string
            format: uuid
          required: true
          description: The UUID of the routine.
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: Routine details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Routine'
        '400':
          description: Invalid routine ID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            AI routines or AI query generation are not enabled for the organization, the acting user lacks routines access (the Use routines role permission) on every model, or a user-scoped API key tried to access another user's routine.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Routine not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      summary: Update an AI Routine
      description: |
        Update an existing AI Routine. All request fields are optional, and only supplied fields are changed.
      operationId: routineUpdate
      tags:
        - AI Routines
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          description: The UUID of the routine.
          name: routineId
          in: path
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                condition:
                  type: object
                  nullable: true
                  description: |
                    Replaces the delivery condition: Omni authors and verifies the condition query, resets the comparison baseline, and cancels any in-progress condition evaluation so a run started under the old condition cannot deliver. 
                    
                    Pass null to remove the condition, making the routine deliver on every scheduled run. 
                    
                    Omit to leave the stored condition unchanged.
                  properties:
                    conditionPrompt:
                      type: string
                      minLength: 1
                      maxLength: 2000
                      description: The delivery condition in plain language. Omni authors and verifies a semantic query from this text.
                      example: there are failed orders
                  required:
                    - conditionPrompt
                name:
                  type: string
                  minLength: 1
                  maxLength: 512
                  description: New customer-visible name of the routine. Used as the email subject for email destinations, and shown on Slack deliveries.
                description:
                  type: string
                  nullable: true
                  maxLength: 2000
                  description: Display-only notes about the routine. Pass null to clear it.
                destination:
                  allOf:
                    - $ref: '#/components/schemas/RoutineDestination'
                    - description: Replaces the routine's full recipient configuration with the supplied destination.
                prompt:
                  type: string
                  minLength: 1
                  description: |
                    New natural language prompt for the routine: what Omni runs and delivers on each scheduled run. Run a skill as part of the routine by including a skill's `/identifier`, which will resolve as the routine owner when the routine fires.
                schedule:
                  type: string
                  minLength: 1
                  description: New six-field cron expression (minute, hour, day-of-month, month, day-of-week, year; use `?` for an unspecified day field). Minimum frequency is once per hour.
                timezone:
                  type: string
                  minLength: 1
                  description: New IANA timezone identifier used to evaluate the schedule.
      responses:
        '200':
          description: Routine updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Routine'
        '400':
          $ref: '#/components/responses/RoutineConditionApiError400'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '402':
          $ref: '#/components/responses/AiCreditShutoffError'
        '403':
          description: |
            AI routines or AI query generation are not enabled for the organization, the routine owner lacks routines access (the Use routines role permission) on the routine model, or a user-scoped API key tried to update another user's routine.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Routine or routine-owner membership not found, or the routine has been deleted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/RoutineConditionApiError503'
    delete:
      summary: Delete an AI Routine
      description: |
        Delete an AI Routine. The routine will immediately stop running. This action cannot be undone.
      operationId: routineDelete
      tags:
        - AI Routines
      parameters:
        - name: id
          in: path
          schema:
            type: string
            format: uuid
            description: The UUID of the routine.
          required: true
          description: The UUID of the routine.
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: Routine deleted successfully.
          content:
            application/json:
              schema:
                type: object
                required:
                  - deleted
                  - id
                properties:
                  deleted:
                    type: boolean
                    enum:
                      - true
                    description: Always true on a successful delete.
                  id:
                    type: string
                    format: uuid
                    description: The deleted routine’s ID.
        '400':
          description: Invalid routine ID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: AI routines or AI query generation are not enabled for the organization, or a user-scoped API key tried to delete another user's routine.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Routine not found or has already been deleted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/ai/routines/{routineId}/trigger:
    post:
      tags:
        - AI Routines
      summary: Trigger AI Routine
      description: |
        Run an existing routine immediately, in addition to its schedule — e.g. to get an off-cycle result or verify a routine produces the email you expect.

        Executes once using the routine owner's permissions and delivers the AI response to every recipient configured on the routine. A conditional routine evaluates its delivery condition first, so the run may complete without delivering.
        
        Returns as soon as the run has started; the result is delivered asynchronously.
      security:
        - bearerAuth: []
      operationId: routineTrigger
      parameters:
        - name: routineId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the routine.
        - $ref: '#/components/parameters/userId'
      responses:
        '202':
          description: The run has started. For a conditional routine this is the condition evaluation, which delivers only if the condition is met.
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                properties:
                  id:
                    type: string
                    format: uuid
                    description: The ID of the run (scheduled job) that was started.
                    example: 990e8400-e29b-41d4-a716-446655440004     
        '400':
          description: |
            Bad request. Possible causes:
            
            - Invalid routine ID
            - Routine cannot run as configured (e.g. its model, branch, or owner is no longer accessible)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: AI routines or AI query generation are not enabled for the organization, the acting user lacks routines access (the Use routines role permission) on every model, or a user-scoped API key tried to run another user's routine.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: The routine does not exist or cannot be triggered (deleted, paused, or disabled by Omni).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '409':
          description: A run is already in progress for this routine.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'

  /v1/ai/search-omni-docs:
    post:
      description: |
        Search the Omni documentation using AI to answer questions about Omni features, configuration, modeling, dashboards, and more. Sends a natural language question and returns a synthesized answer with source links to the relevant documentation pages.
      operationId: aiSearchOmniDocs
      summary: Search Omni docs
      tags:
        - AI
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                question:
                  type: string
                  minLength: 1
                  maxLength: 2000
                  description: A natural language question about Omni features, configuration, modeling, dashboards, or other topics covered in the Omni documentation.
                  example: How do I create a dashboard filter?
              required:
                - question
      responses:
        '200':
          description: Documentation search completed successfully. Returns a synthesized answer with source links.
          content:
            application/json:
              schema:
                type: object
                required:
                  - answer
                  - sources
                properties:
                  answer:
                    type: string
                    description: A synthesized answer to the question, based on the Omni documentation.
                    example: |
                      To create a dashboard filter, navigate to your dashboard and click the "Add Filter" button...
                  sources:
                    type: array
                    description: List of documentation pages that were used to synthesize the answer.
                    items:
                      type: object
                      properties:
                        title:
                          type: string
                          description: The title of the source documentation page.
                          example: Dashboard Filters
                        url:
                          type: string
                          format: uri
                          description: URL of the source documentation page.
                          example: https://docs.omni.co/visualize-present/dashboards/filters
                      required:
                        - title
                        - url
        '400':
          description: Invalid request. The question may be missing or exceed the 2000 character limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '402':
          description: |
            AI is unavailable because the organization, the acting user, or that user's embed entity is over its AI credit limit. The body carries the stable reason code `shutoff`.
          content:
            application/json:
              schema:  # AiCreditShutoffError
                type: object
                required:
                  - code
                  - detail
                  - status
                properties:
                  code:
                    type: string
                    enum:
                      - shutoff
                    description: Stable reason code identifying an AI-credit shutoff.
                    example: shutoff
                  detail:
                    type: string
                    description: Human-readable error message describing what went wrong.
                    example: The AI credit limit has been reached. Contact your administrator for assistance.
                  status:
                    type: integer
                    description: HTTP status code of the error.
                    example: 402
        '403':
          description: Omni Agent is not enabled for this organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '500':
          description: AI service error.

  /v1/api-keys:
    get:
      tags:
        - API Tokens
      summary: List API tokens
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        List API tokens in the organization - including Organization API keys, Personal Access Tokens, and MCP OAuth grants - with optional filtering by type. Only metadata - not actual secret values - is returned.
      security:
        - bearerAuth: []
      operationId: listApiKeys
      parameters:
        - name: type
          in: query
          schema:
            type: string
            enum:
              - organization
              - personal
              - mcp
          description: |
            Filter by token type. Omit to return all types.

            - `organization` - Organization-level API key. Not tied to a specific user.
            - `personal` - Personal access token. Acts as a specific user.
            - `mcp` - MCP OAuth grant issued during the OAuth authorization flow
        - name: cursor
          in: query
          schema:
            type: string
            format: uuid
          description: Cursor for pagination.
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Number of items to return per page.
        - name: sortField
          in: query
          schema:
            type: string
            enum:
              - createdAt
              - name
            default: createdAt
          description: Field to sort by.
        - name: sortDirection
          in: query
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
          description: Sort direction.
      responses:
        '200':
          description: API tokens retrieved successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: Unique ID for the token.
                        name:
                          type: string
                          description: Token name.
                        type:
                          type: string
                          enum:
                            - organization
                            - personal
                            - mcp
                          description: Token type.
                        enabled:
                          type: boolean
                          description: Whether the token is enabled. `false` for disabled Organization API keys; PATs and MCP tokens are always `true`.
                        createdAt:
                          type: string
                          format: date-time
                          description: ISO 8601 timestamp of when the token was created.
                        membershipId:
                          type: string
                          format: uuid
                          nullable: true
                          description: Membership ID of the user who owns the token. `null` for Organization API keys.
              example:
                pageInfo:
                  hasNextPage: false
                  nextCursor: null
                  pageSize: 50
                  totalRecords: 2
                records:
                  - id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                    name: CI deployment key
                    type: organization
                    enabled: true
                    createdAt: "2026-01-15T10:00:00.000Z"
                    membershipId: null
                  - id: b2c3d4e5-f6a7-8901-bcde-f12345678901
                    name: Personal token
                    type: personal
                    enabled: true
                    createdAt: "2026-02-10T14:30:00.000Z"
                    membershipId: c3d4e5f6-a7b8-9012-cdef-123456789012
        '400':
          description: |
            Bad Request. Validation errors include:

            - `type: Invalid option: expected one of "organization"|"personal"|"mcp"`
            - `cursor: Invalid UUID`
            - `pageSize: Page size must be at least 1`
            - `pageSize: Page size cannot exceed 100`
            - `pageSize: Invalid input: expected number, received NaN`
            - `sortField: Invalid option: expected one of "createdAt"|"name"`
            - `sortDirection: Invalid option: expected one of "asc"|"desc"`
            - `Bad authorization header, must be formatted as Bearer <token>`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: type: Invalid option: expected one of \"organization\"|\"personal\"|\"mcp\""
                status: 400
        '403':
          description: |
            Forbidden. Requires Organization Admin permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "Forbidden: Requires Organization Admin permissions"
                status: 403
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/api-keys/{id}:
    get:
      tags:
        - API Tokens
      summary: Retrieve an API token
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Get a single API token by its ID.
      responses:
        '200':
          description: Token found successfully.
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                  - name
                  - type
                  - enabled
                  - createdAt
                  - membershipId
                properties:
                  id:
                    type: string
                    format: uuid
                    description: The token's unique identifier.
                  name:
                    type: string
                    description: The name given when the token was created. Not unique within an organization.
                  type:
                    type: string
                    enum:
                      - organization
                      - personal
                      - mcp
                    description: |
                      Token type:
                      - `organization` - Organization API key
                      - `personal` - Personal Access Token (PAT)
                      - `mcp` - MCP OAuth PAT
                  enabled:
                    type: boolean
                    description: Whether the token can currently authenticate. Organization Admins can disable any token type; disabled tokens remain visible but cannot authenticate.
                  createdAt:
                    type: string
                    format: date-time
                    description: ISO 8601 timestamp of when the token was created (UTC).
                  membershipId:
                    type: string
                    format: uuid
                    nullable: true
                    description: The user membership the token is scoped to. `null` if `type` is `organization`.
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                name: CI deployment key
                type: organization
                enabled: true
                createdAt: "2026-04-20T10:00:00.000Z"
                membershipId: null
        '400':
          description: |
            Bad Request. The ID parameter is not a valid UUID, or the Authorization header is malformed.

            Possible error messages:
            - `Bad Request: id: Invalid UUID`
            - `Bad authorization header, must be formatted as Bearer <token>`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: id: Invalid UUID"
                status: 400
        '401':
          description: |
            Unauthorized. The Authorization header is missing or malformed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '403':
          description: |
            Forbidden. Authentication succeeded but the caller is not an organization admin, or the bearer token is invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "Forbidden: Requires Organization Admin permissions"
                status: 403
        '404':
          description: |
            Not Found. No token with that ID exists in the caller's organization.

            The same `404` response is returned regardless of whether the token exists in a different organization - no tenant information is leaked.

            Example error message:
            - `Api key with id 00000000-0000-0000-0000-000000000000 does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "Api key with id 00000000-0000-0000-0000-000000000000 does not exist"
                status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - API Tokens
      summary: Delete API token
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Permanently delete an API token. Works for all token types — Organization API keys, Personal Access Tokens (PATs), and MCP OAuth grants. This is the same as revoking a token in the Omni app.

        Concurrent deletes are safe — the first caller receives `200` and the second receives `404` consistently. An Organization Admin can delete the token they authenticated with, which is useful for self-service rotation.
      security:
        - bearerAuth: []
      operationId: deleteApiKey
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: UUID of the token to revoke. The token must belong to the same organization as the authenticating user.
      responses:
        '200':
          description: Token revoked successfully. The row is deleted; subsequent `GET` returns `404`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    description: Human-readable description of the outcome.
                  success:
                    type: boolean
                    description: Always `true` on a successful revocation.
              example:
                message: API token revoked
                success: true
        '400':
          description: |
            Bad Request. The `id` is not a UUID, or the `Authorization` header is missing/malformed.

            Possible error messages:
            - `Bad Request: id: Invalid UUID`
            - `Bad authorization header, must be formatted as Bearer <token>`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: id: Invalid UUID"
                status: 400
        '403':
          description: |
            Forbidden. The caller is not an organization admin.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "Requires Organization Admin permissions"
                status: 403
        '404':
          description: |
            Not Found. No token with that id exists in the caller's organization, or another admin revoked it first.

            Possible error messages:
            - `Api key with id <id> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "Api key with id 00000000-0000-0000-0000-000000000000 does not exist"
                status: 404
        '405':
          description: |
            Method Not Allowed. HTTP method other than `GET`, `PUT`, or `DELETE`.

            Possible error messages:
            - `Method not allowed`
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                  status:
                    type: integer
              example:
                detail: Method not allowed
                status: 405
        '429':
          $ref: '#/components/responses/TooManyRequests'

    put:
      tags:
        - API Tokens
      summary: Enable or disable API token
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Disable or re-enable an API token. To permanently revoke a token, use the [Delete API token](/api/api-tokens/delete-api-token) endpoint.

        This operation is **idempotent**: calling it twice with the same body produces the same result.
      security:
        - bearerAuth: []
      operationId: updateApiKey
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: UUID of the API token. The token must exist in the authenticating user's organization.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - enabled
              properties:
                enabled:
                  type: boolean
                  description: |
                    Determines if the API token is enabled: `false` to disable the token, `true` to re-enable it.
              additionalProperties: false
            examples:
              disable:
                summary: Disable a token
                value:
                  enabled: false
              enable:
                summary: Enable a token
                value:
                  enabled: true
      responses:
        '200':
          description: Token updated successfully. Returns the current state of the token, or the same state if already in the requested state.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: Unique ID for the token.
                  name:
                    type: string
                    description: Token name.
                  type:
                    type: string
                    enum:
                      - organization
                      - personal
                      - mcp
                    description: Token type.
                  enabled:
                    type: boolean
                    description: Whether the token is enabled.
                  createdAt:
                    type: string
                    format: date-time
                    description: ISO 8601 timestamp of when the token was created.
                  membershipId:
                    type: string
                    format: uuid
                    nullable: true
                    description: Membership ID of the user who owns the token. `null` for Organization API keys.
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                name: CI deployment key
                type: organization
                enabled: false
                createdAt: "2026-04-20T10:00:00.000Z"
                membershipId: null
        '400':
          description: |
            Bad Request. Validation errors include:

            - `id: Invalid UUID` - The token ID is not a valid UUID format
            - `enabled: Invalid input: expected boolean, received string` - Non-boolean value provided for `enabled`
            - `Unrecognized key: "<field>"` - Unknown field in request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              examples:
                invalidUuid:
                  summary: Malformed UUID
                  value:
                    detail: "Bad Request: id: Invalid UUID"
                    status: 400
                nonBooleanValue:
                  summary: Non-boolean enabled value
                  value:
                    detail: "Bad Request: enabled: Invalid input: expected boolean, received string"
                    status: 400
                unknownField:
                  summary: Unknown field in request body
                  value:
                    detail: "Bad Request: Unrecognized key: \"name\""
                    status: 400
        '403':
          description: |
            Forbidden. Requires Organization Admin permissions, or authentication failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "Requires Organization Admin permissions"
                status: 403
        '404':
          description: |
            Not Found. The token does not exist or belongs to a different organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "Api key with id 00000000-0000-0000-0000-000000000000 does not exist"
                status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/ip-allowlist:
    get:
      description: |
        <Note>
          **Organization Admin** permissions are required for this endpoint.
        </Note>

        Get the organization's IP allowlist: whether it's enforced, the Omni support and SCIM bypasses, and the allowed addresses with their notes.
      operationId: ipAllowlistGet
      summary: Get the IP allowlist
      tags:
        - IP Allowlist
      responses:
        '200':
          description: The current IP allowlist settings.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IpAllowlist'
              example:
                enabled: true
                allowsSupport: false
                exemptsScim: true
                entries:
                  - address: 203.0.113.0/24
                    note: Boston VPN
                  - address: 198.51.100.7/32
                    note: null
        '400':
          description: Missing or malformed `Authorization` header.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              examples:
                bad_auth_header:
                  summary: Malformed Authorization header
                  value:
                    error:
                      code: 400
                      message: "Bad authorization header, must be formatted as Bearer <token>"
        '403':
          description: Invalid API key, or the caller is not an organization admin.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              examples:
                invalid_api_key:
                  summary: Invalid bearer token
                  value:
                    error:
                      code: 403
                      message: "Invalid bearer token"
                not_admin:
                  summary: Not an organization admin
                  value:
                    detail: "Managing the IP allowlist requires organization admin access"
                    status: 403
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

    patch:
      description: |
        <Note>
          **Organization Admin** permissions are required for this endpoint.
        </Note>

        Update the organization's IP allowlist. Every field is optional, but at least one must be provided; omit a field to leave it unchanged. `entries` replaces the whole list, notes included.
      operationId: ipAllowlistUpdate
      summary: Update the IP allowlist
      tags:
        - IP Allowlist
      x-mint:
        content: |
          ## Update requirements and checks
          
          The update is refused if it would leave the allowlist enabled with no entries, or enabled with a list that doesn't cover the address this request came from. 
          
          The check runs on any update that leaves the list enabled, including `{"enabled": true}` on its own, not only on updates to `entries`. The check only confirms that the address sending the request is on the list. It doesn't check your admins' addresses. A script that runs from a listed server can enable or replace the list and lock every admin out of Omni.
      requestBody:
        required: true
        content:
          application/json:
            schema:  # IpAllowlistUpdateBody
              type: object
              properties:
                allowsSupport:
                  type: boolean
                  description: Whether Omni support staff, signing in through Omni's own support login, can reach the organization from outside the list.
                  example: false
                enabled:
                  type: boolean
                  description: Whether the allowlist is enforced. While false the entries are kept, but no address is blocked.
                  example: true
                entries:
                  type: array
                  items:  # IpAllowlistEntryInput
                    type: object
                    properties:
                      address:
                        type: string
                        minLength: 1
                        description: A public IPv4 or IPv6 address or CIDR range. A bare address allows only that address.
                        example: 203.0.113.0/24
                      note:
                        type: string
                        maxLength: 200
                        description: What the entry is for, up to 200 characters.
                        example: Boston VPN
                    required:
                      - address
                    additionalProperties: false
                  description: Replaces the whole list, notes included. Each address may appear only once.
                exemptsScim:
                  type: boolean
                  description: Whether SCIM provisioning requests (everything under `/api/scim/`) are accepted from any address. They still need an organization API key.
                  example: true
              additionalProperties: false
            examples:
              replace_entries:
                summary: Replace the list, with notes
                value:
                  entries:
                    - address: 203.0.113.0/24
                      note: Boston VPN
                    - address: 2001:db8::/32
                      note: Okta
              pause_enforcement:
                summary: Pause enforcement, keeping the list
                value:
                  enabled: false
      responses:
        '200':
          description: Settings updated. Returns the stored settings.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IpAllowlist'
              example:
                enabled: true
                allowsSupport: false
                exemptsScim: true
                entries:
                  - address: 203.0.113.0/24
                    note: Boston VPN
                  - address: 2001:db8::/32
                    note: Okta
        '400':
          description: |
            Invalid request. Common causes:

            - A missing or malformed `Authorization` header
            - Invalid JSON body
            - An empty body
            - An unknown field
            - A blank address
            - An address that isn't a public IP address or CIDR range
            - An address listed twice
            - A note over 200 characters
            - An enabled list with no entries
            - An enabled list that doesn't cover the address this request came from
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              examples:
                bad_auth_header:
                  summary: Malformed Authorization header
                  value:
                    error:
                      code: 400
                      message: "Bad authorization header, must be formatted as Bearer <token>"
                private_range:
                  summary: Private address
                  value:
                    detail: "Only public addresses can be allowlisted: 10.0.0.0/8"
                    status: 400
                lockout:
                  summary: Would block the calling address
                  value:
                    detail: "The address this request came from (203.0.113.7) is not covered by the list, so saving it would block that address. Add it, or a range that contains it, first."
                    status: 400
        '403':
          description: Invalid API key, or the caller is not an organization admin.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              examples:
                invalid_api_key:
                  summary: Invalid bearer token
                  value:
                    error:
                      code: 403
                      message: "Invalid bearer token"
                not_admin:
                  summary: Not an organization admin
                  value:
                    detail: "Managing the IP allowlist requires organization admin access"
                    status: 403
        '409':
          description: The allowlist changed while this request was being handled. Nothing was written; retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
              example:
                detail: "The IP allowlist changed while this request was being handled. Nothing was written; retry."
                status: 409
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/connections:
    post:
      tags:
        - Connections
      summary: Create connection
      description: |
        Creates a new database connection. See the **Parameters**
      security:
        - bearerAuth: []
      operationId: createConnection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - dialect
                - name
              properties:
                dialect:
                  $ref: '#/components/schemas/Dialect'
                name:
                  type: string
                  description: A descriptive name for the connection
                host:
                  type: string
                  description: |
                    The hostname or IP address of the database server.
                    - **MotherDuck** - Not required
                    - **Snowflake** - Provide only the account identifier (e.g., `myaccount` not `myaccount.snowflakecomputing.com`)
                    - **BigQuery** - Automatically determined from the service account
                port:
                  type: integer
                  description: |
                    The port number for the database connection.

                    Not required for Snowflake, MotherDuck, BigQuery, Databricks, and Athena.
                database:
                  type: string
                  description: |
                    The default database/catalog to connect to.

                    - **BigQuery** - Provide the project ID
                    - **Athena** - Provide the data catalog
                username:
                  type: string
                  description: |
                    The username to authenticate with.

                    - **MotherDuck** - Not required
                    - **BigQuery** - Provide client email from the service account
                passwordUnencrypted:
                  type: string
                  description: |
                    **Required for all dialects except BigQuery, Snowflake, and Athena.** The password to authenticate with. Must not be blank, except for Trino, where a blank value is allowed.

                    - **BigQuery** - This must be the JSON service account key file content
                    - **Snowflake** with keypair authentication - Can be omitted
                baseRole:
                  type: string
                  description: |
                    The default role for users accessing the connection. Available roles include:

                    - `VIEWER` - Can view the model
                    - `QUERIER` - Can view and query the model
                    - `QUERY_TOPICS` - Can query specific topics. Equivalent to **Restricted Querier.**
                    - `MODELER` - Can edit and model the data
                    - `CONNECTION_ADMIN` - Full administrative access to the connection
                    - `NO_ACCESS` - No access to the model
                    - [Custom roles](/administration/users/custom-roles) defined for your organization
                warehouse:
                  type: string
                  description: |
                    **Required for**:

                    - **Snowflake** - Specify the warehouse
                    - **Databricks** - Specify the HTTP path
                includeSchemas:
                  type: array
                  items:
                    type: string
                  description: Schemas to include. Omit this field or send an empty array to include all schemas.
                includeOtherCatalogs:
                  type: array
                  items:
                    type: string
                  description: |
                    Other catalogs/databases to include. 
                    
                    **Only applicable for databases that support multi-catalog queries:** BigQuery, Snowflake, MotherDuck, Databricks, Trino, Athena.
                defaultSchema:
                  type: string
                  description: |
                    **Required for MSSQL.** The default schema to use. 
                queryTimeoutSeconds:
                  type: integer
                  description: The timeout in seconds for queries. Maximum value is `3600` (1 hour). Only applicable for databases that support query timeouts.
                  maximum: 3600
                  default: 900
                region:
                  type: string
                  description: |
                    **Required for BigQuery and Athena connections.**

                    - **BigQuery** - Specify a region like `us`
                    - **Athena** - Specify an AWS region like `us-east-1`
                maxBillingBytes:
                  type: integer
                  minimum: 0
                  maximum: 9007199254740991
                  description: |
                    **Applicable for BigQuery.** Maximum bytes that can be billed for a BigQuery query.
                scratchSchema:
                  type: string
                  description: Schema to use for data input (upload) tables. If not specified, a suitable default will be chosen.
                systemTimezone:
                  type: string
                  description: The timezone to use for the system.
                  default: "UTC"
                queryTimezone:
                  type: string
                  description: The timezone to use for queries.
                  default: "NONE"
                allowsUserSpecificTimezones:
                  type: boolean
                  description: Whether to allow users to specify their own timezones.
                  default: false
                alwaysScopeViewNames:
                  type: boolean
                  description: |
                    Whether to prefix generated view names with their schema and catalog, e.g. `my_catalog_public__orders` instead of `orders`. Defaults to `true` for dialects that support multiple catalogs, and `false` otherwise. See [View name generation](/connect-data/view-name-generation) for more information.
                trustServerCertificate:
                  type: boolean
                  description: |
                    **Applicable for MSSQL, Exasol, and ClickHouse.** Whether to trust the server certificate.
                  default: false
                privateKey:
                  type: string
                  description: |
                    **Applicable for Snowflake only.** An unencrypted RSA key for keypair authentication, in PKCS#8 or PKCS#1 PEM format. Omni converts a PKCS#1 key to PKCS#8, and automatically adds PKCS#8 PEM headers if none are provided.
                acceptsLicense:
                  type: boolean
                  description: |
                    **Applicable for Oracle.** Whether to accept the license terms. 
                authenticationType:
                  type: string
                  description: |
                    **Applicable for BigQuery, MSSQL, Snowflake, Databricks, and Athena.** The authentication method to use.
                awsRoleArn:
                  type: string
                  description: |
                    **Applicable for Athena.** The AWS role ARN to assume for the connection.
                enableDbSemanticLayerIntegration:
                  type: boolean
                  description: |
                    Whether to enable the database's semantic layer integration:

                    - **Snowflake** - [Snowflake semantic views](/connect-data/snowflake-semantic-views)
                    - **Databricks** - [Databricks Unity Catalog](/connect-data/databricks-unity-catalog-integration)
                enableDbSemanticLayerTopics:
                  type: boolean
                  description: |
                    **Applicable for Snowflake and Databricks.** Whether to enable database semantic layer topics.
                externalOauthAudience:
                  type: string
                  description: |
                    **Applicable for Snowflake.** The audience for [external OAuth authentication](/connect-data/oauth/snowflake/external-okta).
                externalOauthAuthorizationUrl:
                  type: string
                  description: |
                    **Applicable for Snowflake.** The authorization URL for [external OAuth authentication](/connect-data/oauth/snowflake/external-okta).
                externalOauthTokenUrl:
                  type: string
                  description: |
                    **Applicable for Snowflake.** The token URL for [external OAuth authentication](/connect-data/oauth/snowflake/external-okta).
                hostOverride:
                  type: string
                  description: |
                    **Applicable for Snowflake.** Override for the host value.
                accountIdentifier:
                  type: string
                  description: |
                    **Applicable for Snowflake.** The Snowflake account identifier. Optional when used with `hostOverride` and `port`. Snowflake can usually infer the account identifier from the hostname, so this is only needed for key-pair authentication through a proxy, tunnel, or PrivateLink where the hostname doesn't identify the account.
                inferRelationshipsFromColumnNames:
                  type: boolean
                  default: true
                  description: |
                    Whether to automatically infer relationships from column-name conventions during schema refresh.
                inferRelationshipsFromForeignKeys:
                  type: boolean
                  description: |
                    **Applicable for Postgres and Snowflake.** Whether to automatically infer relationships from foreign key constraints.
                oauthClientId:
                  type: string
                  description: |
                    **Applicable for [Snowflake](/connect-data/oauth/snowflake/native) and Databricks.** The OAuth client ID for native OAuth.
                oauthClientSecretUnencrypted:
                  type: string
                  description: |
                    **Applicable for Snowflake and Databricks.** The unencrypted OAuth client secret for [native OAuth](/connect-data/oauth/snowflake/native).
                offloadedSchemas:
                  type: array
                  items:
                    type: string
                  description: |
                    **Applicable for all dialects.** Schemas to offload. See [Offloading schemas](/connect-data/offloading-schemas) for more information.
                useMachineAuth:
                  type: boolean
                  description: |
                    **Applicable for Athena and Databricks.** Whether to use machine authentication.
                useReadScaling:
                  type: boolean
                  description: |
                    **Applicable for MSSQL only.** Whether to request read-only routing (`ApplicationIntent=ReadOnly`) for the connection, so read queries can run on a readable secondary.

                    SQL Server uses the primary when no readable secondary is available. Omni's own upload writes always use the primary, even when this setting is on. When you test the connection, Omni shows a warning, without failing, if it can't verify that the session is read-only.
                  default: false
            examples:
              BigQuery:
                summary: BigQuery
                value:
                  dialect: bigquery
                  name: My BigQuery Connection
                  region: us
                  passwordUnencrypted: "<SERVICE_ACCOUNT_JSON_FILE>"
                  defaultSchema: my_dataset
                  includeSchemas: [dataset1, dataset2]
                  includeOtherCatalogs: [other_project1, other_project2]
                  maxBillingBytes: 1000000000
              MySQL:
                summary: MySQL
                value:
                  dialect: mysql
                  name: My MySQL Connection
                  host: mysql.example.com
                  port: 3306
                  database: mydb
                  username: dbuser
                  passwordUnencrypted: mypassword
                  includeSchemas: [public, analytics]
                  queryTimeoutSeconds: 900
              Postgres:
                summary: PostgreSQL
                value:
                  dialect: postgres
                  name: My Postgres Connection
                  host: postgres.example.com
                  port: 5432
                  database: mydb
                  username: dbuser
                  passwordUnencrypted: mypassword
                  includeSchemas: [public, analytics]
                  queryTimeoutSeconds: 900
              Snowflake:
                summary: Snowflake
                value:
                  dialect: snowflake
                  name: My Snowflake Connection
                  host: myaccount
                  database: MYDB
                  username: dbuser
                  passwordUnencrypted: mypassword
                  warehouse: COMPUTE_WH
                  includeSchemas: [PUBLIC, ANALYTICS]
                  includeOtherCatalogs: [OTHER_DB1, OTHER_DB2]
                  queryTimeoutSeconds: 900
      responses:
        '201':
          description: Connection created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: string
                    format: uuid
                    description: Connection ID
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - Connections
      summary: List connections
      description: Retrieves a list of database connections with optional filtering and sorting
      security:
        - bearerAuth: []
      operationId: listConnections
      parameters:
        - name: name
          in: query
          schema:
            type: string
          description: Filter connections by name (case-insensitive, partial matching)
        - name: database
          in: query
          schema:
            type: string
          description: Filter connections by database name (case-insensitive, partial matching)
        - name: dialect
          in: query
          schema:
            type: string
            pattern: '^(?:\s*(bigquery|mysql|postgres|redshift|exasol|snowflake|motherduck|mssql|fabric|databricks|databricks_lakebase|clickhouse|trino|athena|starrocks|mariadb|oracle|sap_hana)\s*(?:,\s*(bigquery|mysql|postgres|redshift|exasol|snowflake|motherduck|mssql|fabric|databricks|databricks_lakebase|clickhouse|trino|athena|starrocks|mariadb|oracle|sap_hana)\s*)*)?$'
          description: Filter connections by dialect(s). For multiple dialects, provide a comma-separated list (e.g., `postgres,mysql`).
        - name: sortField
          in: query
          schema:
            type: string
            enum: [database, dialect, name]
          description: Field to sort by
        - $ref: '#/components/parameters/sortDirection'
        - name: includeDeleted
          in: query
          schema:
            type: boolean
          description: When `true`, the response includes deleted connections. Defaults to `false`.
      responses:
        '200':
          description: Returns a list of connections objects
          content:
            application/json:
              schema:
                type: object
                properties:
                  connections:
                    type: array
                    items:
                      $ref: '#/components/schemas/Connection'
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `formErrors: Unrecognized key(s) in object: '<invalidParam>'`
            - `sortField: Invalid enum value. Expected 'database' | 'dialect' | 'name', received '<invalidField>'`
            - `Invalid dialect(s): <invalidDialect>`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/connections/{connectionId}:
    get:
      tags:
        - Connections
      summary: Get connection
      description: |
        <Note>
        This endpoint requires **Restricted Querier** permissions or higher.
        </Note>
        
        Fetch a single connection by ID.
      security:
        - bearerAuth: []
      operationId: getConnection
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Connection ID
      responses:
        '200':
          description: Connection retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  connection:
                    $ref: '#/components/schemas/Connection'
              example:
                connection:
                  id: "550e8400-e29b-41d4-a716-446655440000"
                  name: "Production Snowflake"
                  dialect: "snowflake"
                  database: "analytics_db"
                  defaultSchema: "public"
                  deletedAt: null
                  baseRole: "QUERIER"
                  branchConnectionEnvironmentOverridesUserAttr: false
                  environmentConnectionSwitchesSchemaModel: false
                  userAttributeNameForConnectionEnvironments: null
                  userAttributeValuesForDefaultEnvironment: null
        '400':
          description: Invalid connection ID format (not a valid UUID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden. Possible error messages:
            
            - `Caller lacks `READ` permission on the connection` - Requesting user has insufficient permissions for the connection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    patch:
      tags:
        - Connections
      summary: Update connection
      description: |
        Update connection properties including credentials. Credentials are encrypted at rest and never returned in API responses.

        This endpoint supports credential rotation for infrastructure-as-code workflows. Depending on the connection type, you can update:
        - **Password-based connections** (Postgres, MySQL, etc.): Use `passwordUnencrypted` to update the password
        - **BigQuery**: Use `passwordUnencrypted` to update the service account JSON
        - **Snowflake**: Use `privateKey` to update the RSA private key (PKCS#8 or PKCS#1 PEM format, minimum 2048 bits). Omni converts a PKCS#1 key to PKCS#8.

        At least one field must be provided in the request body.
      security:
        - bearerAuth: []
      operationId: updateConnection
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Connection ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                passwordUnencrypted:
                  type: string
                  description: |
                    Password or service account JSON for credential rotation.

                    - For **password-based connections** such as Postgres, MySQL, etc. -  Provide the new password as a string
                    - For **BigQuery** - Provide the entire service account JSON file as a string
                  example: "new-password-here"
                privateKey:
                  type: string
                  description: |
                    **Only applicable to Snowflake connections**. Unencrypted RSA private key for Snowflake connections, in PKCS#8 or PKCS#1 PEM format. Must be at least 2048 bits. Omni converts a PKCS#1 key to PKCS#8.
                  example: "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg..."
                baseRole:
                  type: string
                  description: |
                    The default role for users accessing the connection. Available roles include:

                    - `VIEWER` - Can view the model
                    - `QUERIER` - Can view and query the model
                    - `QUERY_TOPICS` - Can query specific topics. Equivalent to **Restricted Querier.**
                    - `MODELER` - Can edit and model the data
                    - `CONNECTION_ADMIN` - Full administrative access to the connection
                    - `NO_ACCESS` - No access to the model
                    - [Custom roles](/administration/users/custom-roles) defined for your organization
                environmentUserAttribute:
                  oneOf:
                    - type: object
                      properties:
                        attributeName:
                          type: string
                          description: The name of the user attribute to use for environments
                        defaultValues:
                          type: array
                          items:
                            type: string
                          description: Array of default values for the user attribute
                      required:
                        - attributeName
                        - defaultValues
                    - type: "null"
                  description: Configuration for environment user attributes. Set to `null` to remove environment user attribute settings.
                useReadScaling:
                  type: boolean
                  description: |
                    **Applicable for MSSQL only.** Whether to request read-only routing (`ApplicationIntent=ReadOnly`) for the connection, so read queries can run on a readable secondary.

                    SQL Server uses the primary when no readable secondary is available. Omni's own upload writes always use the primary, even when this setting is on.
            examples:
              rotatePassword:
                summary: Rotate password (Postgres, MySQL, etc.)
                value:
                  passwordUnencrypted: "new-password-here"
              rotateBigQueryKey:
                summary: Rotate BigQuery service account key
                value:
                  passwordUnencrypted: '{"type":"service_account","project_id":"my-gcp-project","private_key_id":"abc123...","private_key":"-----BEGIN PRIVATE KEY-----\nMIIEvgIBA...\n-----END PRIVATE KEY-----\n","client_email":"my-service@my-gcp-project.iam.gserviceaccount.com","client_id":"123456789","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/..."}'
              rotateSnowflakeKey:
                summary: Rotate Snowflake keypair
                value:
                  privateKey: "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg..."
              updateRole:
                summary: Update base role
                value:
                  baseRole: "QUERIER"
              combinedUpdate:
                summary: Update credentials and role together
                value:
                  passwordUnencrypted: "new-password-here"
                  baseRole: "MODELER"
      responses:
        '200':
          description: Connection updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Updated connection credentials."
        '400':
          description: |
            Bad request. Common causes:
            - Empty or invalid request body
            - `privateKey` used with non-Snowflake connection
            - Invalid private key format or insufficient key length
            - Empty private key provided
            - Encrypted private key provided — Omni rejects it with a message to remove the passphrase
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden. User does not have Connection Admin permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Connections
      summary: Delete connection
      description: |
        <Note>
          This endpoint requires **Connection Admin** permissions.
        </Note>
        
        Archives a connection (moves it to trash). Archived connections can be restored from the trash in the Omni app.

        This endpoint performs a soft delete. The connection and its associated models and content are moved to trash, and sensitive information such as passwords and keys is permanently deleted.
      security:
        - bearerAuth: []
      operationId: deleteConnection
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Connection ID
      responses:
        '200':
          description: Connection moved to trash successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
              example:
                success: true
                message: "Connection moved to trash."
        '400':
          description: Invalid connection ID format (not a valid UUID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden. Caller lacks Connection Admin permissions on the connection.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            The connection does not exist or belongs to another organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '410':
          description: |
            Gone

            The connection has already been archived.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/connection-environments:
    post:
      tags:
        - Connection environments
      summary: Create connection environments
      description: Creates connection environments by associating environment-specific connections with a base connection
      security:
        - bearerAuth: []
      operationId: createConnectionEnvironments
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - baseConnectionId
                - environmentConnectionIds
              properties:
                baseConnectionId:
                  type: string
                  format: uuid
                  description: The UUID of the base connection to associate environment connections with
                environmentConnectionIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                    minLength: 1
                    pattern: \S
                  minItems: 1
                  description: |
                    An array of connection UUIDs to use as environment connections. All connection IDs must be valid UUIDs that:

                    - Exist in your organization
                    - Have the same dialect as the base connection
                    - Aren't already used as environment connections for the base connection
                    - Aren't the same as the base connection
            example:
              baseConnectionId: "123e4567-e89b-12d3-a456-426614174000"
              environmentConnectionIds:
                - "223e4567-e89b-12d3-a456-426614174001"
                - "323e4567-e89b-12d3-a456-426614174002"
      responses:
        '201':
          description: Connection environment(s) created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        environmentConnectionId:
                          type: string
                          format: uuid
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Invalid method`
            - `Invalid JSON`
            - `baseConnectionId: Base connection ID must be a valid UUID`
            - `environmentConnectionIds: All environment connection IDs must be valid UUIDs`
            - `environmentConnectionIds: Must contain at least 1 value`
            - `environmentConnectionIds: Values must not be blank`
            - `The following environment connection IDs are not valid for this connection: <ids>`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: |
            Not Found

            Possible error messages:
            - `Connection with id <baseConnectionId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/connection-environments/{connectionEnvironmentId}:
    put:
      tags:
        - Connection environments
      summary: Update connection environment
      description: |
        Associates user attribute values with a specific connection environment, which will determine when it is used.
      security:
        - bearerAuth: []
      x-mint:
        content: |
          <Note>
            User attribute values for environment connections work in conjunction with the user attribute name and values set on the base connection, which can be defined using the [Update connection endpoint](/api/connections/update-connection).
            
            The base connection specifies which user attribute to evaluate, while each environment connection specifies which values of that attribute will trigger its use.
          </Note>
      operationId: updateConnectionEnvironment
      parameters:
        - name: connectionEnvironmentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the connection environment to update
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userAttributeValues
              properties:
                userAttributeValues:
                  type: array
                  items:
                    type: string
                    minLength: 1
                    pattern: \S
                  minItems: 1
                  description: |
                    An array of user attribute values to associate with the connection environment. For example: `["dev", "staging"]`

                    **Note**: User attribute values must be unique across all environments for a base connection.
      responses:
        '200':
          description: Connection environment updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Invalid JSON`
            - `Invalid method`
            - `Invalid uuid`
            - `userAttributeValues: Must contain at least 1 value`
            - `userAttributeValues: Values must not be blank`
            - `User attribute value "<value>" is already in use`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Connection environment with id <id> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

    delete:
      tags:
        - Connection environments
      summary: Delete connection environment
      description: |
        Deletes a connection environment by ID. Requires Connection Admin or Organization Admin permissions.
      security:
        - bearerAuth: []
      operationId: deleteConnectionEnvironment
      parameters:
        - name: connectionEnvironmentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the connection environment to delete
      responses:
        '200':
          description: Connection environment deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '403':
          description: |
            Forbidden

            The authenticated user does not have sufficient permissions to delete this connection environment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Connection environment with id <id> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /unstable/documents/{dashboardId}/export:
    get:
      tags:
        - Content migration
      summary: Export dashboard
      description: |
        Export dashboard for migration between Omni instances.
      x-mint:
        content: |
          <Warning>
            This API is in beta and may have future breaking changes.
          </Warning>

          ## Color palettes

          Custom color palette references in the dashboard spec (`_scheme`, `markColorScheme`, and raw Vega `scheme` fields) are rewritten from organization-specific palette UUIDs to portable, export-local keys (for example, `export-palette-0`). The `colorPalettes` field in the response gives the name, type, and colors for each key, so the destination organization can match or recreate the palette on import. References to palettes that have since been deleted are not included in the export.
      security:
        - orgApiKey: []
      operationId: exportDashboard
      parameters:
        - name: dashboardId
          in: path
          required: true
          schema:
            type: string
          description: Dashboard identifier (miniUuid or full UUID)
          example: abc123
      responses:
        '200':
          description: Dashboard export data
          content:
            application/json:
              schema:
                type: object
                required:
                  - colorPalettes
                  - document
                  - exportVersion
                  - queryModels
                properties:
                  colorPalettes:
                    type: object
                    additionalProperties:
                      $ref: '#/components/schemas/ColorPaletteExport'
                    description: Custom color palettes referenced by the dashboard's visualization configuration, keyed by export-local key (for example, `export-palette-0`). References to deleted palettes are omitted.
                  dashboard:
                    description: Dashboard configuration and layout
                  document:
                    type: object
                    description: Document metadata
                    properties:
                      ephemeral:
                        type: string
                      name:
                        type: string
                    required:
                      - name
                  exportVersion:
                    type: string
                    description: Export format version
                    example: "0.1"
                  fileUploads:
                    type: object
                    additionalProperties: {}
                    description: Spreadsheet file data included in the export. Each item contains the file content and metadata needed to restore spreadsheet tiles during import.
                  queryModels:
                    type: object
                    additionalProperties: {}
                    description: Query model definitions referenced by the dashboard
                  workbookModel:
                    description: Workbook model data
              example:
                document:
                  name: Q3 revenue dashboard
                exportVersion: "0.1"
                dashboard: {}
                queryModels: {}
                colorPalettes:
                  export-palette-0:
                    name: Brand colors
                    type: discrete
                    colors:
                      - "#1f77b4"
                      - "#ff7f0e"
                      - "#2ca02c"
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Permission denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'

  /unstable/documents/import:
    post:
      tags:
        - Content migration
      summary: Import dashboard
      description: |
        Import dashboard to Omni instance.
      x-mint:
        content: |
          <Warning>
            This API is in beta and may have future breaking changes.
          </Warning>

          ## Color palettes

          If the export references custom color palettes, Omni resolves each referenced palette against the destination organization by exact (trimmed) name and type:

          - **Identical colors** — The existing palette is reused.
          - **Different colors, or more than one palette sharing that name with none matching by color** — The import fails with `400` before anything is written. The error message names the palette.
          - **No matching palette** — Omni creates one. Creating a palette during import requires the caller to have **Editor** or **Manager** permissions; otherwise, the import fails with `403` before anything is written.

          The response's `colorPalettes` field lists which referenced palettes were created versus reused.

          If a UUID doesn't resolve to an existing palette in the destination organization, it's dropped and the chart falls back to the organization's default palette.

          ## Presentation settings

          `document` also carries any `metadata.presentation` object from the export (dashboard-level presentation settings, including the control bar's `collapsible` / `defaultExpanded` / `fixed` state), which Omni writes onto the new workbook. Feature flags and generated descriptions from the source instance are not imported.
      security:
        - orgApiKey: []
      operationId: importDashboard
      requestBody:
        required: true
        content:
          application/json:
            schema:  # DocumentsImportBody
              type: object
              properties:
                baseModelId:
                  type: string
                  format: uuid
                  description: Base model ID for the imported document
                colorPalettes:
                  type: object
                  additionalProperties:
                    $ref: '#/components/schemas/ColorPaletteExport'
                  description: Custom color palettes referenced by visualization configurations, keyed by export-local key. Include the `colorPalettes` field from the export response to carry custom palettes to the destination organization.
                dashboard:  # DashboardExport
                  type: object
                  properties:
                    crossfilterEnabled:
                      type: boolean
                      description: Crossfilter enabled
                    dashboardCustomTheme:  # DashboardCustomThemeExport
                      type: object
                      nullable: true
                      properties:
                        customThemeId:
                          type: string
                          format: uuid
                          description: Custom theme applied to the dashboard
                      required:
                        - customThemeId
                    facetFilters:
                      type: boolean
                      description: Facet filters enabled
                    metadata:  # DashboardMetadata
                      type: object
                      properties:
                        containers:
                          type: array
                          items:
                            anyOf:
                              - $ref: '#/components/schemas/GridContainer'
                              - $ref: '#/components/schemas/PageContainer'
                              - $ref: '#/components/schemas/StackContainer'
                          description: Container configuration
                        hiddenTiles:
                          type: array
                          items:
                            type: string
                          description: IDs of hidden tiles
                        layouts:  # DashboardExportLayouts
                          type: object
                          properties:
                            lg:
                              type: array
                              items:
                                $ref: '#/components/schemas/DashboardExportLayoutItem'
                              description: Large screen layout
                            sm:
                              type: array
                              items:
                                $ref: '#/components/schemas/DashboardExportLayoutItem'
                              description: Small screen layout
                          required:
                            - lg
                        messages:
                          type: object
                          properties:
                            queryError:
                              type: string
                              description: Custom error message
                            queryNoResults:
                              type: string
                              description: Custom no results message
                          description: Custom messages
                        runQueriesOn:
                          type: string
                          enum:
                            - current-page
                            - all-pages
                          description: Run queries on scope
                        textTiles:
                          type: array
                          items:
                            $ref: '#/components/schemas/TextTile'
                          description: Text tiles in dashboard
                        tileControlMap:
                          type: object
                          additionalProperties:
                            type: object
                            additionalProperties:
                              anyOf:
                                - type: string
                                - type: boolean
                                  enum:
                                    - false
                          description: Tile control mappings
                        tileFilterMap:
                          type: object
                          additionalProperties:
                            type: object
                            additionalProperties:
                              anyOf:
                                - type: string
                                - type: boolean
                                  enum:
                                    - false
                          description: Tile filter mappings
                        tileSettings:
                          type: object
                          additionalProperties: {}
                          description: Tile settings
                      required:
                        - hiddenTiles
                        - layouts
                        - textTiles
                        - tileControlMap
                        - tileFilterMap
                        - tileSettings
                      description: Dashboard metadata
                    metadataVersion:
                      allOf:
                        - $ref: '#/components/schemas/DashboardMetadataVersion'
                        - description: Metadata version
                    queryPresentationCollection:  # QueryPresentationCollection
                      type: object
                      properties:
                        filterConfig:
                          description: Filter configuration
                        filterOrder:
                          type: array
                          items:
                            type: string
                          description: Filter order
                        queryPresentationCollectionMemberships:
                          type: array
                          items:  # QueryPresentationMembership
                            type: object
                            properties:
                              queryPresentation:  # QueryPresentationExport
                                type: object
                                properties:
                                  aiConfig:
                                    type: object
                                    properties:
                                      description:
                                        type: object
                                        properties:
                                          aiContext:
                                            type: string
                                          enabled:
                                            type: boolean
                                      subTitle:
                                        type: object
                                        properties:
                                          aiContext:
                                            type: string
                                          enabled:
                                            type: boolean
                                    description: AI metadata configuration
                                  automaticVis:
                                    type: boolean
                                    nullable: true
                                    description: Auto visualization enabled
                                  description:
                                    type: string
                                    nullable: true
                                    description: Query description
                                  editingModelObjectName:
                                    type: string
                                    nullable: true
                                    description: Model object the tab edits
                                  editingModelObjectNameChange:
                                    type: string
                                    nullable: true
                                    description: Pending rename of the edited model object
                                  fileUploadId:
                                    type: string
                                    nullable: true
                                    format: uuid
                                    description: Associated file upload ID
                                  filterOrder:
                                    type: array
                                    items:
                                      type: string
                                    description: Display order of filters on the tab
                                  isSql:
                                    type: boolean
                                    nullable: true
                                    description: Is raw SQL query
                                  miniUuid:
                                    type: string
                                    description: Mini UUID
                                  name:
                                    type: string
                                    description: Query name
                                  prefersChart:
                                    type: boolean
                                    description: Prefers chart view
                                  query:  # QueryWrapper
                                    type: object
                                    properties:
                                      queryJson:
                                        description: Query JSON definition
                                  resultConfig:
                                    description: Table display configuration
                                  sourceQueryPresentationTabIdx:
                                    type: integer
                                    nullable: true
                                    description: Tab index of the source query presentation for linked tabs
                                  subTitle:
                                    type: string
                                    nullable: true
                                    description: Tile subtitle
                                  topicName:
                                    type: string
                                    nullable: true
                                    description: Topic name
                                  type:
                                    type: string
                                    enum:
                                      - blank
                                      - csv
                                      - query
                                      - dataset
                                      - spreadsheet
                                      - sql
                                      - dbt
                                      - query-view
                                      - linked
                                      - foreign
                                    description: Query type
                                  visConfig:
                                    description: Visualization config
                                required:
                                  - automaticVis
                                  - isSql
                                  - name
                                  - prefersChart
                                  - query
                                  - topicName
                            required:
                              - queryPresentation
                          description: Query presentations
                      required:
                        - filterOrder
                        - queryPresentationCollectionMemberships
                    refreshInterval:
                      type: integer
                      nullable: true
                      exclusiveMinimum: 0
                      description: Auto-refresh interval in seconds
                  required:
                    - metadataVersion
                  description: Dashboard configuration from export
                document:
                  type: object
                  description: Document metadata from export. Also accepts a `metadata.presentation` object (dashboard presentation settings such as the control bar) to restore onto the new workbook.
                  properties:
                    description:
                      type: string
                      nullable: true
                      description: Document description
                    ephemeral:
                      type: string
                      description: Ephemeral content identifier
                    metadata:
                      type: object
                      properties:
                        presentation:
                          $ref: '#/components/schemas/DocumentPresentation'
                      description: Workbook metadata from the export. Only `presentation` (the dashboard's control bar settings) is imported.
                    name:
                      type: string
                      description: Document name
                  required:
                    - name
                exportVersion:
                  type: string
                  enum:
                    - '0.1'
                  description: Export format version (must be `0.1`)
                fileUploads:
                  type: object
                  additionalProperties:  # FileUploadExport
                    type: object
                    properties:
                      fileData:
                        type: object
                        properties: {}
                        additionalProperties: {}
                        description: JSON content of the spreadsheet
                      fileName:
                        type: string
                        description: File name
                    required:
                      - fileData
                      - fileName
                  description: Spreadsheet file data to restore during import. Include the `fileUploads` field from the export response to restore spreadsheet tiles in the imported dashboard.
                folderPath:
                  type: string
                  description: Destination folder path
                identifier:
                  allOf:
                    - $ref: '#/components/schemas/DocumentIdentifier'
                    - description: Custom identifier for the imported document. A 2–48 character slug.
                queryModels:
                  type: object
                  additionalProperties:
                    type: object
                  default: {}
                  description: Query model definitions from export. Defaults to `{}` if omitted.
                workbookModel:
                  type: object
                  description: Workbook model from export. Required.
              required:
                - baseModelId
                - dashboard
                - document
                - exportVersion
                - workbookModel
      
            example:
              baseModelId: 3c90c3cc-0d44-4b50-8888-8dd25736052a
              document:
                name: Q3 revenue dashboard
              exportVersion: "0.1"
              dashboard:
                metadataVersion: 2
              workbookModel: {}
              queryModels: {}
              colorPalettes:
                export-palette-0:
                  name: Brand colors
                  type: discrete
                  colors:
                    - "#1f77b4"
                    - "#ff7f0e"
                    - "#2ca02c"
      responses:
        '201':
          description: Document imported successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - documentId
                  - identifier
                properties:
                  colorPalettes:
                    type: object
                    properties:
                      created:
                        type: array
                        items:
                          $ref: '#/components/schemas/ImportedColorPalette'
                        description: Palettes that didn't match an existing one and were created in this organization
                      reused:
                        type: array
                        items:
                          $ref: '#/components/schemas/ImportedColorPalette'
                        description: Palettes matched by name, type, and colors to an existing palette in this organization
                    required:
                      - created
                      - reused
                    description: Custom palettes the import created in this organization or matched by name and type
                  documentId:
                    type: string
                    format: uuid
                    description: ID of the imported document
                  identifier:
                    type: string
                    description: Document identifier (miniUuid)
      
              example:
                documentId: 91c6b1ee-df10-4d59-9a4b-6a6e0f5b9df2
                identifier: xyz789
                colorPalettes:
                  created: []
                  reused:
                    - id: 3c90c3cc-0d44-4b50-8888-8dd25736052a
                      key: export-palette-0
                      name: Brand colors
                      type: discrete
        '400':
          description: |
            Invalid export data.

            Possible causes:

            - A required field (`baseModelId`, `dashboard`, `document`, `exportVersion`, or `workbookModel`) is missing or malformed
            - A referenced color palette's name matches an existing palette in the destination organization, but the colors differ (the error message names the palette)
            - A referenced color palette's name matches more than one existing palette, and none of them match by color (the error message names the palette)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages/causes:

            - `User-scoped API keys cannot act on behalf of other users`
            - Operations targeting personal/restricted scope require the user's **Personal content access** setting (`omni_allows_personal_content` system attribute) to be enabled for the user. If not enabled, the API will respond with `403 Forbidden`.
            - Creating a new color palette during import requires the caller to hold **Manage Config** permission
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Base model not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-codeSamples:
        - lang: bash
          label: Import dashboard
          source: |
            curl -X POST "https://{instance}.omniapp.co/api/unstable/documents/import" \
              -H "Authorization: Bearer YOUR_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
                "baseModelId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
                "dashboard": {"metadataVersion": 2},
                "document": {"name": "Q3 revenue dashboard"},
                "exportVersion": "0.1",
                "workbookModel": {}
              }'

  /v1/color-palettes:
    get:
      tags:
        - Color Palettes
      summary: List custom color palettes
      description: |
        List the custom chart color palettes defined for the organization. Built-in palettes are not included.
      operationId: colorPalettesList
      responses:
        '200':
          description: Custom color palettes in the organization
          content:
            application/json:
              schema:
                type: object
                properties:
                  color_palettes:
                    type: array
                    description: Custom palettes, sorted by name
                    items:
                      type: object
                      properties:
                        colors:
                          $ref: '#/components/schemas/ColorPaletteColors'
                        name:
                          $ref: '#/components/schemas/ColorPaletteName'
                        type:
                          $ref: '#/components/schemas/ColorPaletteType'
                        id:
                          $ref: '#/components/schemas/ColorPaletteId'
                        created_at:
                          $ref: '#/components/schemas/ColorPaletteCreatedAt'
                        updated_at:
                          $ref: '#/components/schemas/ColorPaletteUpdatedAt'
                      required:
                        - colors
                        - created_at
                        - id
                        - name
                        - type
                        - updated_at
                required:
                  - color_palettes
              example:
                color_palettes:
                  - id: 3c90c3cc-0d44-4b50-8888-8dd25736052a
                    name: Brand colors
                    type: discrete
                    colors:
                      - "#1f77b4"
                      - "#ff7f0e"
                      - "#2ca02c"
                    created_at: "2026-09-10T10:00:00.000Z"
                    updated_at: "2026-09-10T10:00:00.000Z"
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
        - Color Palettes
      summary: Create custom color palette
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Create a custom chart color palette. Palette names must be unique per type within the organization.
      operationId: colorPalettesCreate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                colors:
                  $ref: '#/components/schemas/ColorPaletteColors'
                name:
                  $ref: '#/components/schemas/ColorPaletteName'
                type:
                  $ref: '#/components/schemas/ColorPaletteType'
              required:
                - colors
                - name
                - type
              additionalProperties: false
            example:
              name: Brand colors
              type: discrete
              colors:
                - "#1f77b4"
                - "#ff7f0e"
                - "#2ca02c"
      responses:
        '201':
          description: Color palette created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ColorPaletteObject'
                required:
                  - colors
                  - created_at
                  - id
                  - name
                  - type
                  - updated_at
              example:
                id: 3c90c3cc-0d44-4b50-8888-8dd25736052a
                name: Brand colors
                type: discrete
                colors:
                  - "#1f77b4"
                  - "#ff7f0e"
                  - "#2ca02c"
                created_at: "2026-09-10T10:00:00.000Z"
                updated_at: "2026-09-10T10:00:00.000Z"
        '400':
          description: |
            Bad Request. Validation errors include:

            - `name: Required`
            - `type: Invalid option: expected one of "discrete"|"continuous"`
            - `colors: Too small: expected array to have >=1 items`
            - `colors: Too big: expected array to have <=50 items`
            - `colors: Invalid string: must match pattern ^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: name: Required"
                status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '403':
          description: |
            Forbidden. Requires Organization Admin permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "Forbidden: Requires Manage Config permission"
                status: 403
        '409':
          description: |
            Conflict. A palette with this name already exists for this type.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
              example:
                detail: "A palette named \"Brand colors\" already exists for type \"discrete\""
                status: 409
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/color-palettes/{colorPaletteId}:
    get:
      tags:
        - Color Palettes
      summary: Get custom color palette
      description: |
        Retrieve a custom chart color palette by ID.
      operationId: colorPalettesGet
      parameters:
        - $ref: '#/components/parameters/colorPaletteId'
      responses:
        '200':
          description: Color palette details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ColorPaletteObject'
        '400':
          description: |
            Bad Request. The `id` parameter is not a valid UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: id: Invalid UUID"
                status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '404':
          description: |
            Not Found. No color palette with that ID exists in the caller's organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "Color palette with id 00000000-0000-0000-0000-000000000000 does not exist"
                status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - Color Palettes
      summary: Update custom color palette
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Update a custom chart color palette. Omitted fields keep their current values. Charts using the palette use up the new colors immediately.
      operationId: colorPalettesUpdate
      parameters:
        - $ref: '#/components/parameters/colorPaletteId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ColorPaletteObject'
            example:
              name: Brand colors (updated)
              colors:
                - "#1f77b4"
                - "#ff7f0e"
                - "#2ca02c"
                - "#d62728"
      responses:
        '200':
          description: Color palette updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ColorPaletteObject'
                required:
                  - colors
                  - created_at
                  - id
                  - name
                  - type
                  - updated_at
              example:
                id: 3c90c3cc-0d44-4b50-8888-8dd25736052a
                name: Brand colors (updated)
                type: discrete
                colors:
                  - "#1f77b4"
                  - "#ff7f0e"
                  - "#2ca02c"
                  - "#d62728"
                created_at: "2026-09-10T10:00:00.000Z"
                updated_at: "2026-09-11T09:15:00.000Z"
        '400':
          description: |
            Bad Request. The request body or the `id` parameter failed validation.

            Possible error messages:
            - `Bad Request: id: Invalid UUID`
            - `colors: Too small: expected array to have >=1 items`
            - `type: Invalid option: expected one of "discrete"|"continuous"`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: id: Invalid UUID"
                status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '403':
          description: |
            Forbidden. Requires Organization Admin permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "Forbidden: Requires Manage Config permission"
                status: 403
        '404':
          description: |
            Not Found. No color palette with that ID exists in the caller's organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "Color palette with id 00000000-0000-0000-0000-000000000000 does not exist"
                status: 404
        '409':
          description: |
            Conflict. Another palette already uses this name and type, or the palette is the organization's default and its type cannot be changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
              example:
                detail: "A palette named \"Brand colors\" already exists for type \"discrete\""
                status: 409
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Color Palettes
      summary: Delete custom color palette
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Delete a custom chart color palette. Charts using it revert to the organization's default palette.
      operationId: colorPalettesDelete
      parameters:
        - $ref: '#/components/parameters/colorPaletteId'
      responses:
        '204':
          description: Color palette deleted
        '400':
          description: |
            Bad Request. The `id` parameter is not a valid UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: id: Invalid UUID"
                status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '403':
          description: |
            Forbidden. Requires Organization Admin permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "Forbidden: Requires Manage Config permission"
                status: 403
        '404':
          description: |
            Not Found. No color palette with that ID exists in the caller's organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "Color palette with id 00000000-0000-0000-0000-000000000000 does not exist"
                status: 404
        '409':
          description: |
            Conflict. The palette is the organization's default palette and cannot be deleted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
              example:
                detail: "The organization's default color palette cannot be deleted"
                status: 409
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/content:
    get:
      tags:
        - Content
      summary: Retrieve content
      description: Retrieve paginated list of documents and folders
      security:
        - bearerAuth: []
      operationId: getContent
      parameters:
        - name: labels
          in: query
          schema:
            type: string
          description: Comma-separated list of label names to filter by
          example: verified,important
        - name: scope
          in: query
          schema:
            type: string
            enum: [restricted, organization]
            default: organization
          description: Content scope filter
        - name: sortField
          in: query
          schema:
            type: string
            enum: [favorites, name]
            default: name
          description: Field to sort by
        - $ref: '#/components/parameters/sortDirection'
        - name: include
          in: query
          schema:
            type: string
            pattern: ^\s*((_count|labels)\s*)?(,\s*((_count|labels)\s*)?)*$
          description: |
            Comma-separated list of additional fields to include in the response:
            - `_count` - Adds count metrics (folders: document and favorite counts; documents: favorite and view counts)
            - `labels` - Includes associated content labels
        - name: folderId
          in: query
          schema:
            type: string
            format: uuid
          description: Returns all content in the specified folder. **Cannot be used with `path`.**
        - name: path
          in: query
          schema:
            type: string
          description: |
            Filter content by path. **Cannot be used with `folderId`.** Examples:
            - `/folder/subfolder` - Returns the folder and any content it contains
            - `/folder/*` - Returns the folder and the direct children only
            - `/folder/**` - Returns all folders and content recursively in the path
            - `/` - Returns all content in the organization
        - name: creatorId
          in: query
          schema:
            type: string
            format: uuid
          description: UUID of organization membership. **Required when `scope` is `restricted`.**
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Number of records per page (1-100)
        - name: cursor
          in: query
          schema:
            type: string
          description: Pagination cursor from previous response
      responses:
        '200':
          description: Paginated content list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Page size must be at least 1`
            - `Page size cannot exceed 100`
            - `Invalid sort field`
            - `creatorId required when scope is restricted`
            - `Unrecognized query parameters`
            - `folderId and path cannot be used together`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:
            - `User with id <uuid> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/content/search:
    get:
      operationId: contentSearch
      summary: Search dashboards and apps
      description: |
        Free-text search over the organization's dashboards and apps. Apps are included only if they are enabled for the organization.
      tags:
        - Content
      parameters:
        - name: limit
          schema:
            type: integer
            exclusiveMinimum: 0
            maximum: 25
            default: 10
            example: 10
          required: false
          description: Max results to return (1-25).
          in: query
        - name: q
          schema:
            type: string
            minLength: 1
            description: Free-text keywords matched against names, descriptions, query names, folder names, labels, and creator names.
            example: revenue by region
          required: true
          description: Free-text keywords matched against names, descriptions, query names, folder names, labels, and creator names.
          in: query
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: Matching dashboards and apps
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        description:
                          type: string
                          nullable: true
                          description: Description, if any.
                          example: "Quarterly revenue breakdown across all regions"
                        folderPath:
                          type: string
                          nullable: true
                          description: Folder path, if any.
                          example: "/Finance/Reports"
                        identifier:
                          type: string
                          description: The document identifier, usable with the documents endpoints. The schedules endpoints accept dashboard results only.
                          example: 12db1a0a
                        name:
                          type: string
                          description: Dashboard or app name.
                          example: "Revenue by region"
                        tileNames:
                          type: array
                          items:
                            type: string
                          description: Up to 5 query names from the document's workbook — the first few, not the ones that matched. For an app, the saved queries it can run.
                          example: ["Revenue Trend", "Regional Breakdown", "YoY Comparison"]
                        type:
                          type: string
                          enum:
                            - app
                            - dashboard
                          description: |
                            Whether the match opens as a dashboard or as an app. Apps are returned only when the organization has apps enabled.

                            If this field is absent, treat the result as a dashboard.
                          example: dashboard
                        url:
                          type: string
                          description: |
                            URL to open the match as the surface its type names (the published dashboard or app), with a redirect query param matching the type: 
                            
                            - `?redirectToDashboard=true` for dashboards
                            - `?redirectToApp=true` for apps
                            
                            Omitted on MCP requests when the the organization has the `disable-ai-mcp-links` feature flag enabled.
                          example: "https://blobsrus.omniapp.co/dashboards/12db1a0a?redirectToDashboard=true"
                        verified:
                          type: boolean
                          description: Whether it carries a verified label.
                          example: true
                    description: Matching dashboards and apps, most relevant first.
                required:
                  - results
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `q is required`
            - `limit must be between 1 and 25`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/content-validator:
    get:
      tags:
        - Content validator
      summary: Validate content
      description: |
        Validate all content against the model and return documents with queries and any validation issues.

        This endpoint scans all documents associated with the model and identifies any fields, views, or other model elements referenced in queries that are no longer valid.

        You can optionally scope validation to only content that references a specific model element (view, field, or topic) using the `find` and `find_type` parameters. This is useful when you only need to validate content using a specific element, avoiding the overhead of validating all documents.
      security:
        - bearerAuth: []
      operationId: validateContent
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the model to validate content against.
        - name: branch_id
          in: query
          schema:
            type: string
            format: uuid
          description: The ID of the branch to validate against. If not provided, validates against the main model.
        - name: userId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            The user ID to act on behalf of. Only valid when using an organization API key.

            User-scoped API keys cannot use this parameter and will receive a 403 error if provided.
        - name: include_personal_folders
          in: query
          schema:
            type: boolean
          description: When enabled, include personal folders in the search.
        - name: find
          in: query
          schema:
            type: string
          description: |
            The name of the model element to find content references for. Must be used together with `find_type`.

            For `FIELD` type, the value must be fully qualified with the view name (e.g., `orders.status`).
        - name: find_type
          in: query
          schema:
            type: string
            enum:
              - VIEW
              - FIELD
              - TOPIC
          description: |
            The type of model element to search for. Must be used together with `find`.

            When both parameters are provided, only content that references the specified element will be validated and included in the response.
        - name: force_full_validation
          in: query
          schema:
            type: boolean
            default: false
          description: |
            By default, validation of a large set of queries only checks field, view, and topic references, which can miss breakage in sorts, pivots, or totals. Enable this parameter to validate every query at full depth. Validation is slower on large models.
        - name: content_filter_mode
          in: query
          schema:
            type: string
            enum:
              - ALL
              - WITH_ISSUES
              - NO_ISSUES
            default: ALL
          description: |
            Filter documents by validation status.

            - `ALL` - Return all documents with at least one query.
            - `WITH_ISSUES` - Return only documents with at least one query issue, dashboard filter issue, or document error.
            - `NO_ISSUES` - Return only documents with zero issues and no document errors.
        - name: labels
          in: query
          schema:
            type: string
          description: |
            Comma-separated list of label names. Documents with any of the specified labels are included.
        - name: folder_paths
          in: query
          schema:
            type: string
            example: '["/Finance","/Marketing"]'
          description: |
            An array of folder paths to restrict validation to. Paths are prefix-matched, so `/Finance` also matches `/Finance/Reports`.

            Documents that aren't in a folder are excluded unless you include an empty string (`""`) in the array.
        - name: creator_id
          in: query
          schema:
            type: string
            format: uuid
          description: |
            Return only documents created by this user. This value is the **membership ID**, not the user ID. Use the [List users](/api/users/list-users) endpoint to retrieve membership IDs.
      responses:
        '200':
          description: Content validation results
          content:
            application/json:
              schema:
                type: object
                properties:
                  model_id:
                    type: string
                    format: uuid
                    description: The ID of the model that was validated.
                  branch:
                    type: object
                    nullable: true
                    description: Branch information if a branch was specified, null otherwise.
                    properties:
                      id:
                        type: string
                        format: uuid
                        description: The branch ID.
                      name:
                        type: string
                        description: The branch name.
                  content:
                    type: array
                    description: List of documents with their validation results.
                    items:
                      type: object
                      properties:
                        document_id:
                          type: string
                          description: The document ID.
                        identifier:
                          type: string
                          description: The document identifier (slug).
                        name:
                          type: string
                          description: The document name.
                        type:
                          type: string
                          description: The document type (e.g., "Published").
                        updated_at:
                          type: string
                          format: date-time
                          description: When the document was last updated.
                        folder:
                          type: object
                          nullable: true
                          description: Folder information if the document is in a folder.
                          properties:
                            name:
                              type: string
                              description: The folder name.
                            path:
                              type: string
                              description: The full folder path.
                        owner:
                          type: object
                          description: Information about the document owner.
                          properties:
                            email:
                              type: string
                              format: email
                              description: The owner's email address.
                            name:
                              type: string
                              description: The owner's display name.
                        require_pull_request_to_publish:
                          type: boolean
                          description: Whether the document requires a pull request to publish changes.
                        queries_and_issues:
                          type: array
                          description: List of queries and their validation issues.
                          items:
                            type: object
                            properties:
                              query_name:
                                type: string
                                description: The name of the query.
                              query_presentation_id:
                                type: string
                                description: The query presentation ID.
                              query_id_map_key:
                                type: string
                                description: The query ID map key.
                              issues:
                                type: array
                                items:
                                  type: string
                                description: List of validation issue messages for this query.
                        dashboard_filter_issues:
                          type: array
                          items:
                            type: string
                          description: List of validation issues for dashboard filters.
              examples:
                allContent:
                  summary: Validate all content
                  value:
                    model_id: "550e8400-e29b-41d4-a716-446655440000"
                    branch: null
                    content:
                      - document_id: "abc123"
                        identifier: "dashboard-1"
                        name: "Sales Dashboard"
                        type: "Published"
                        updated_at: "2025-01-15T10:00:00Z"
                        folder:
                          name: "Reports"
                          path: "/Reports"
                        owner:
                          email: "user@example.com"
                          name: "Jane Doe"
                        require_pull_request_to_publish: false
                        queries_and_issues:
                          - query_name: "Total Revenue"
                            query_presentation_id: "qp-123"
                            query_id_map_key: "1"
                            issues:
                              - "Field 'orders.old_field' not found in model"
                        dashboard_filter_issues: []
                filteredByField:
                  summary: Validate content using a specific field
                  description: When using find=orders.status&find_type=FIELD, only content that references the orders.status field is validated.
                  value:
                    model_id: "550e8400-e29b-41d4-a716-446655440000"
                    branch: null
                    content:
                      - document_id: "def456"
                        identifier: "order-analysis"
                        name: "Order Analysis"
                        type: "Published"
                        updated_at: "2025-01-15T11:30:00Z"
                        folder:
                          name: "Analytics"
                          path: "/Analytics"
                        owner:
                          email: "analyst@example.com"
                          name: "John Smith"
                        require_pull_request_to_publish: false
                        queries_and_issues:
                          - query_name: "Orders by Status"
                            query_presentation_id: "qp-456"
                            query_id_map_key: "1"
                            issues: []
                        dashboard_filter_issues: []
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `modelId: Invalid UUID`
            - `Both 'find' and 'find_type' parameters must be provided together`
            - `When find_type is FIELD, the find parameter must be scoped by view name (e.g., view_name.field_name)`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Bad Request: modelId: Invalid UUID"
                status: 400
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `User-scoped API keys cannot act on behalf of other users`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User-scoped API keys cannot act on behalf of other users"
                status: 403
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Shared model with id <modelId> does not exist`
            - `User with id <creator_id> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                modelNotFound:
                  summary: Model not found
                  value:
                    detail: "Shared model with id 550e8400-e29b-41d4-a716-446655440000 does not exist"
                    status: 404
                creatorNotFound:
                  summary: Unrecognized creator_id
                  value:
                    detail: "User with id 550e8400-e29b-41d4-a716-446655440000 does not exist"
                    status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
        - Content validator
      summary: Find and replace content
      description: |
        Performs find/replace operations on content using the specified model.

        This endpoint allows you to replace views or fields across all documents associated with the model. This is useful when renaming model elements and need to update all references.
      security:
        - bearerAuth: []
      operationId: findReplaceContent
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the model to perform find/replace against.
        - name: userId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            The user ID to act on behalf of. Only valid when using an organization API key.

            User-scoped API keys cannot use this parameter and will receive a 403 error if provided.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - find
                - replacement
                - find_or_replace_type
              properties:
                find:
                  type: string
                  description: |
                    The value to find. Used with `find_or_replace_type` to scope the search:

                    - `VIEW` - The name of the view to find
                    - `FIELD` - The fully qualified name of the field to find, specified as `view_name.field_name`
                    - `TOPIC` - The name of the topic to find
                replacement:
                  type: string
                  description: |
                    The replacement value.

                    - `VIEW` - The name of the replacement view
                    - `FIELD` - The fully qualified name of the replacement field, specified as `view_name.field_name`
                    - `TOPIC` - The name of the replacement topic
                find_or_replace_type:
                  type: string
                  enum:
                    - VIEW
                    - FIELD
                    - TOPIC
                  description: |
                    The type of find/replace operation.

                    - `VIEW` - Replace view references
                    - `FIELD` - Replace field references. `find` and `replacement` values must be the fully qualified name of the field, e.g. `view_name.field_name`.
                    - `TOPIC` - Replace topic references
                branch_id:
                  type: string
                  format: uuid
                  description: The branch ID to perform the operation on. If not provided, operates on the main model.
                creator_id:
                  type: string
                  format: uuid
                  description: |
                    Restrict replacement to documents created by this user. This value is the **membership ID**, not the user ID. Use the [List users](/api/users/list-users) endpoint to retrieve membership IDs.
                include_personal_folders:
                  type: boolean
                  description: When enabled, include personal folders.
                only_in_workbook_id:
                  type: string
                  format: uuid
                  description: |
                    When provided, scopes the find/replace action to the specified workbook.

                    Must be the workbook model's internal **UUID** — visible in the model IDE URL as `/model/{uuid}`. This is **not** the same as the document's URL identifier (slug) that appears in the dashboard or workbook URL (for example, `/dashboards/abc123`). **Passing a document identifier / slug will silently return 0 replacements instead of raising an error.**
                  example: "550e8400-e29b-41d4-a716-446655440000"
            examples:
              replaceView:
                summary: Replace a view
                value:
                  find: "old_view"
                  replacement: "new_view"
                  find_or_replace_type: "VIEW"
              replaceField:
                summary: Replace a field
                value:
                  find: "orders.old_field"
                  replacement: "orders.new_field"
                  find_or_replace_type: "FIELD"
              replaceViewInBranch:
                summary: Replace a view in a branch
                value:
                  find: "old_view"
                  replacement: "new_view"
                  find_or_replace_type: "VIEW"
                  branch_id: "550e8400-e29b-41d4-a716-446655440001"
      responses:
        '200':
          description: Find/replace operation completed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  replaced_queries_count:
                    type: integer
                    description: The number of queries that had replacements made.
                  replaced_documents_count:
                    type: integer
                    description: The number of documents that had replacements made.
                  replaced_workbook_models_count:
                    type: integer
                    description: The number of workbook models that had replacements made.
                  replaced_dashboard_filters_count:
                    type: integer
                    description: The number of dashboard filters and app controls that had replacements made.
                  skipped_pr_required_count:
                    type: integer
                    description: The number of documents skipped because they require a pull request to publish.
              example:
                replaced_queries_count: 15
                replaced_documents_count: 5
                replaced_workbook_models_count: 3
                replaced_dashboard_filters_count: 2
                skipped_pr_required_count: 0
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `modelId: Invalid UUID`
            - `Find field must be scoped by view name.`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                invalidModelId:
                  summary: Invalid model ID
                  value:
                    detail: "Bad Request: modelId: Invalid UUID"
                    status: 400
                fieldNotScoped:
                  summary: Field not scoped by view name
                  value:
                    detail: "Find field must be scoped by view name."
                    status: 400
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `User-scoped API keys cannot act on behalf of other users`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User-scoped API keys cannot act on behalf of other users"
                status: 403
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Shared model with id <modelId> does not exist`
            - `User with id <creator_id> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                modelNotFound:
                  summary: Model not found
                  value:
                    detail: "Shared model with id 550e8400-e29b-41d4-a716-446655440000 does not exist"
                    status: 404
                creatorNotFound:
                  summary: Unrecognized creator_id
                  value:
                    detail: "User with id 550e8400-e29b-41d4-a716-446655440000 does not exist"
                    status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/dashboards/{dashboardId}/download:
    post:
      tags:
        - Dashboard downloads
      summary: Initiate dashboard or tile download
      description: |
        Start an asynchronous download job for a published dashboard, a dashboard draft, or single tile with support for multiple output formats. After starting a job, poll [Check download status](/api/dashboard-downloads/check-download-status) for completion, then retrieve the file from [Download file](/api/dashboard-downloads/download-file).

        <Note>
          Only one download per dashboard per user is allowed at a time. If a download is already in progress for the specified dashboard, the request will return a `409 Conflict` response with the existing job ID.
        </Note>
      x-mint:
        href: /api/dashboard-downloads/initiate-download
        content: |
          ## Downloading drafts

          A draft's unpublished content renders against the branch its currently on. Permissions and the download setting come from the published dashboard, whether you request the published version or a draft.
      security:
        - bearerAuth: []
      operationId: initiateDashboardDownload
      parameters:
        - name: dashboardId
          in: path
          required: true
          schema:
            type: string
          description: The dashboard identifier (ID or document UUID). Accepts a published dashboard or one of its drafts.
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            The user ID to run the download as. Only valid when authenticating with an organization API key. Use the [List users](/api/users/list-users) or [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - format
              properties:
                format:
                  type: string
                  enum: [pdf, png, csv, xlsx, json]
                  description: |
                    Output format:

                    | Format | Full dashboard | Single tile | Notes |
                    |--------|----------------|-------------|-------|
                    | PDF    | Yes            | Yes         |       |
                    | PNG    | Yes            | Yes         |       |
                    | XLSX   | Yes            | Yes         | For tiles with subtotal or grand total rows, one dimension cell in each totals row is labeled `Total` |
                    | CSV    | Yes (as ZIP)   | Yes         | For full dashboards, delivery will be a zip file containing one CSV per tile. For tiles with subtotal or grand total rows, one dimension cell in each totals row is labeled `Total` |
                    | JSON   | No             | Yes         | Only single tile is supported. Requires `queryIdentifierMapKey` to be specified. |
                filename:
                  type: string
                  maxLength: 255
                  description: Custom filename for the downloaded file. Defaults to the dashboard name.
                queryIdentifierMapKey:
                  type: string
                  description: |
                    Tile identifier to download a single tile instead of the full dashboard. Required for XLSX and JSON formats if `overrideRowLimit=true`.
                filterConfig:
                  type: object
                  description: Dashboard filter values to apply before rendering
                containerPages:
                  type: array
                  items:
                    type: string
                  minItems: 1
                  description: |
                    **Applicable to PDF, PNG, CSV, and XLSX formats, on dashboards using the advanced (multi-page) layout.** The `instanceKey`s of the pages to render:

                    - PDF renders the listed pages in storage order.
                    - PNG accepts exactly one page.
                    - CSV and XLSX keep only the tiles on the selected pages.

                    Not supported for `json` format, `link_only`, or together with `queryIdentifierMapKey` for a single-tile download. Defaults to the dashboard's first page for PDF and PNG, and to every tile for CSV and XLSX.
                paperFormat:
                  type: string
                  enum: [fit_page, letter, legal, tabloid, a3, a4]
                  default: fit_page
                  description: |
                    **Applicable to PDF and PNG formats**. Page size.
                paperOrientation:
                  type: string
                  enum: [portrait, landscape]
                  description: | 
                    **Applicable to PDF and PNG formats**. Page orientation.
                hideTitle:
                  type: boolean
                  default: false
                  description: |
                    **Applicable to PDF and PNG formats**. If `true`, hide the dashboard title in the output.
                showFilters:
                  type: boolean
                  default: true
                  description: |
                    **Applicable to PDF and PNG formats**. If `true`, display applied filter values in the output.
                expandAiSummaries:
                  $ref: '#/components/schemas/ExpandAiSummaries'
                expandTablesToShowAllRows:
                  type: boolean
                  description: |
                    **Applicable to PDF and PNG formats**. If `true`, expand table tiles to display all rows.

                    With `paperFormat: fit_page`, the page grows to fit the rows, and the download fails if the page would be taller than 60,000px.
                singleColumnLayout:
                  type: boolean
                  description: |
                    **Applicable to PDF and PNG formats**. If `true`, render tiles in a single column layout.
                enableFormatting:
                  type: boolean
                  default: false
                  description: |
                    **Applicable to CSV, XLSX, and JSON formats**. If `true`, preserve number and date formatting.
                hideHiddenFields:
                  type: boolean
                  default: false
                  description: |
                    **Applicable to CSV and XLSX formats**. If `true`, exclude hidden fields from the output.
                overrideRowLimit:
                  type: boolean
                  default: false
                  description: |
                    **Applicable to CSV, XLSX, and JSON formats**. Used with `maxRowLimit`. If `true`, remove the default row limit.

                    If `true` for XLSX and JSON formats, a `queryIdentifierMapKey` is required.
                maxRowLimit:
                  type: integer
                  minimum: 1
                  maximum: 1000000
                  description: |
                    **Applicable to CSV, XLSX, and JSON formats**. Maximum number of rows to export. Can be used with `overrideRowLimit` to export more rows than the default row limit.
                cache:
                  type: string
                  default: Standard
                  enum: [Standard, SkipRequery, SkipCache]
                  description: |
                    Cache policy controlling how the download's queries use cached results. Must be one of:

                    - `Standard` - **Default**. Standard caching behavior, including requerying cached result sets.
                    - `SkipRequery` - Uses cached results if available, but does not requery cached result sets.
                    - `SkipCache` - Bypasses the cache and always executes fresh queries.
                useCache:
                  type: boolean
                  deprecated: true
                  description: |
                    Deprecated. Use `cache: SkipCache` for fresh data.
            examples:
              fullDashboardPdf:
                summary: Download full dashboard as PDF
                value:
                  format: pdf
                  paperFormat: letter
                  paperOrientation: landscape
              singleTileJson:
                summary: Download single tile as JSON
                value:
                  format: json
                  queryIdentifierMapKey: "1"
              csvWithOptions:
                summary: Download as CSV with formatting
                value:
                  format: csv
                  enableFormatting: true
                  overrideRowLimit: true
                  maxRowLimit: 50000
              pageDownload:
                summary: Download a single page from an advanced-layout dashboard
                value:
                  format: pdf
                  containerPages:
                    - "01KXH5TG58BB5JBJHGTPC62RZA"
              freshData:
                summary: Download with fresh data (bypass cache)
                value:
                  format: csv
                  cache: SkipCache
      responses:
        '200':
          description: Download initiated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id:
                    type: string
                    format: uuid
                    description: The job ID to use for checking status and downloading the file
                  message:
                    type: string
                    description: Success message
              example:
                job_id: "550e8400-e29b-41d4-a716-446655440000"
                message: "Download initiated successfully"
        '400':
          description: |
            Bad Request. Possible causes:

            - Missing required `format` field
            - Invalid format value
            - Invalid options for the specified format
            - `Invalid page found in containerPages: <page>. Available pages are: <id> (<name>), ...`
            - `PNG format supports only one page`
            - `containerPages can only be used with PDF, PNG, CSV or Excel formats` — also returned when `containerPages` is used on a dashboard that doesn't use the advanced (multi-page) layout
            - Malformed JSON body
            - Invalid UUID format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions to download the specified dashboard
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Dashboard not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          description: A download is already in progress for the specified dashboard
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    description: Error message
                  existing_job_id:
                    type: string
                    format: uuid
                    description: The ID of the existing in-progress job
                  status:
                    type: string
                    description: The status of the existing job
              example:
                detail: "A download is already in progress for this dashboard. Please wait for it to complete or check its status."
                existing_job_id: "550e8400-e29b-41d4-a716-446655440000"
                status: "EXECUTING"
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/dashboards/{dashboardId}/download/{jobId}/status:
    get:
      tags:
        - Dashboard downloads
      summary: Check download status
      x-mint: 
        content: |
          Retrieve the current status of a dashboard or tile download job. Poll this endpoint to determine when the file is ready.

          The response will contain one of the following statuses:

          | Status        | Description                                    | Next Step                        |
          |---------------|------------------------------------------------|----------------------------------|
          | `in_progress` | Job is still processing                    | Continue polling                 |
          | `complete`    | File is ready                              | Call the [Download endpoint](/api/dashboard-downloads/download-file)       |
          | `error`       | Job failed - see `error` field for details | Review error and retry if needed |

          <Tip>
            We recommend the following when polling:

            - Use a reasonable polling interval (2-5 seconds)
            - Avoid polling more frequently than once per second
          </Tip>
      security:
        - bearerAuth: []
      operationId: getDashboardDownloadStatus
      parameters:
        - name: dashboardId
          in: path
          required: true
          schema:
            type: string
          description: The dashboard identifier. This must match the ID of the original download request.
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The job ID returned from the [Initiate dashboard download endpoint](/api/dashboard-downloads/initiate-download)
      responses:
        '200':
          description: Job status retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id:
                    type: string
                    format: uuid
                    description: The job ID
                  status:
                    type: string
                    enum: [in_progress, complete, error]
                    description: Current status of the download job
                  format:
                    type: string
                    enum: [pdf, png, csv, xlsx, json]
                    description: The requested output format
                  created_at:
                    type: string
                    format: date-time
                    description: When the job was created
                  error:
                    type: string
                    description: Error message. Only present when status is `error`.
              examples:
                inProgress:
                  summary: Job in progress
                  value:
                    job_id: "550e8400-e29b-41d4-a716-446655440000"
                    status: "in_progress"
                    format: "pdf"
                    created_at: "2024-01-15T10:30:00Z"
                complete:
                  summary: Job complete
                  value:
                    job_id: "550e8400-e29b-41d4-a716-446655440000"
                    status: "complete"
                    format: "pdf"
                    created_at: "2024-01-15T10:30:00Z"
                error:
                  summary: Job failed
                  value:
                    job_id: "550e8400-e29b-41d4-a716-446655440000"
                    status: "error"
                    format: "pdf"
                    created_at: "2024-01-15T10:30:00Z"
                    error: "All queries failed."
                fitPageTooTall:
                  summary: Job failed - fit-page page too tall
                  value:
                    job_id: "550e8400-e29b-41d4-a716-446655440000"
                    status: "error"
                    format: "pdf"
                    created_at: "2024-01-15T10:30:00Z"
                    error: "The dashboard is too tall to fit on one page. Choose a paper size or turn off expanded content."
        '400':
          description: Invalid job ID format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Job not found or does not belong to the specified dashboard
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/dashboards/{dashboardId}/download/{jobId}:
    get:
      tags:
        - Dashboard downloads
      summary: Download file
      x-mint: 
        content: |
          <Note>
            Only call this endpoint when the [Check download status endpoint](/api/dashboard-downloads/check-download-status) returns a `complete` status.
          </Note>

          Retrieve the completed dashboard or tile download file.

          The response will include appropriate headers for the file type:

          - `Content-Type` -  MIME type based on format (e.g., `application/pdf`)
          - `Content-Disposition` - Attachment with filename (e.g., `attachment; filename="Dashboard Name.pdf"`)
          - `Content-Length` - File size in bytes (when available)
      security:
        - bearerAuth: []
      operationId: downloadDashboardFile
      parameters:
        - name: dashboardId
          in: path
          required: true
          schema:
            type: string
          description: The dashboard identifier. This must match the ID of the original download request.
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The job ID returned from the [Initiate dashboard download endpoint](/api/dashboard-downloads/initiate-download)
      responses:
        '200':
          description: File download successful
          content:
            application/pdf:
              schema:
                type: string
                format: binary
            image/png:
              schema:
                type: string
                format: binary
            text/csv:
              schema:
                type: string
                format: binary
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
            application/json:
              schema:
                type: object
                description: Query result data
            application/zip:
              schema:
                type: string
                format: binary
                description: |
                  **Applicable to full dashboard downloads in CSV format.** ZIP file containing CSV files. 
        '202':
          description: Job still in progress. Poll the [Check download status endpoint](/api/dashboard-downloads/check-download-status) first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '400':
          description: Invalid job ID format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Job not found or does not belong to the specified dashboard
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '410':
          description: Job failed. Call the [Check download status endpoint](/api/dashboard-downloads/check-download-status) for error details.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/dashboards/{dashboardId}/filters:
    get:
      tags:
        - Dashboard filters
      summary: Get dashboard filters and controls
      deprecated: true
      description: |
        <Warning>
          This endpoint is deprecated. Use [Get document](/api/documents-v2/get-document-state) to read a published dashboard's filters and controls from `controls`, or [Get draft](/api/documents-v2/get-draft-state) to read them from a draft.
        </Warning>

        Retrieve the filter and control configuration for a dashboard, including IDs, types, current default values, and metadata.
      security:
        - bearerAuth: []
      operationId: getDashboardFilters
      parameters:
        - name: dashboardId
          in: path
          required: true
          schema:
            type: string
          description: The dashboard identifier
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Optional user ID that attributes the action to the specified user.

            Personal Access Tokens (PATs) cannot use this parameter.
      responses:
        '200':
          description: Dashboard filter and control configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DashboardFiltersResponse'
              example:
                identifier: "abc123"
                filters:
                  order_status:
                    type: "string"
                    kind: "EQUALS"
                    values: ["completed", "pending"]
                    label: "Order Status"
                    description: "Filter by order status"
                    required: false
                    requiredScope: "dashboard"
                    hidden: false
                  order_date:
                    type: "date"
                    kind: "WITHIN_RANGE"
                    left_side: "2024-01-01"
                    right_side: "2024-12-31"
                    label: "Order Date"
                    requiredScope: "tiles"
                controls:
                  - id: "field_selector"
                    type: "FIELD_SELECTION"
                    kind: "FIELD"
                    label: "Metric Selector"
                    field: "orders.revenue"
                    options:
                      - label: "Revenue"
                        value: "orders.revenue"
                      - label: "Quantity"
                        value: "orders.quantity"
                filterOrder: ["order_status", "order_date", "field_selector"]
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions to view the specified dashboard
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Dashboard not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    patch:
      tags:
        - Dashboard filters
      summary: Update dashboard filters/controls
      deprecated: true
      x-mint:
        content: |
          <Warning>
            This endpoint is deprecated. Use [Patch draft](/api/documents-v2/patch-draft) to update a draft's `controls`, then [Publish draft](/api/documents-v2/publish-draft) to publish it. Filters and controls live under `controls` in the v2 documents API, and a `config` in a controls patch replaces the stored config whole.
          </Warning>

          Set or reset values for filters and/or controls on a dashboard. Update specific filters or controls by providing an ID.

          <Note>
            Updates to published dashboards go through a [draft/publish workflow](/content/develop). If a draft already exists for the dashboard, you must set `clearExistingDraft: true` to discard it and proceed with the update.
          </Note>
      security:
        - bearerAuth: []
      operationId: updateDashboardFilters
      parameters:
        - name: dashboardId
          in: path
          required: true
          schema:
            type: string
          description: The dashboard identifier
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Membership ID to act on behalf of.

            Personal Access Tokens (PATs) cannot use this parameter.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DashboardFiltersUpdateRequest'
            examples:
              updateFilterValues:
                summary: Update filter values
                value:
                  filters:
                    order_status:
                      values: ["shipped", "delivered"]
              updateWithClearDraft:
                summary: Update with existing draft (clear draft)
                value:
                  clearExistingDraft: true
                  filters:
                    order_status:
                      values: ["shipped"]
              updateControlLabel:
                summary: Update control label
                value:
                  controls:
                    field_selector:
                      label: "Choose Metric"
              updateDisplayOrder:
                summary: Update display order
                value:
                  filterOrder: ["order_date", "field_selector", "order_status"]
      responses:
        '200':
          description: Updated filter and control configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DashboardFiltersResponse'
        '400':
          description: |
            Bad Request. Possible error messages:

            - `Invalid filter IDs: <ids>. Available filter IDs: <ids>`
            - `Invalid control IDs: <ids>. Available control IDs: <ids>`
            - `Invalid IDs in filterOrder: <ids>. Available IDs: <ids>`
            - `Invalid filter update for <id>: <validation error>`
            - `Invalid control update for <id>: <validation error>`
            - `Request must include at least one filter, control, or filterOrder to update`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions to edit the specified dashboard
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Dashboard not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          description: A draft already exists for this document. Set `clearExistingDraft` to `true` to discard and proceed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/connections/{connectionId}/dbt:
    get:
      operationId: getDbtConfiguration
      summary: Get dbt configuration
      description: |
        <Note>
          **Connection Admin** permissions are required to use this endpoint.
        </Note>

        Retrieve the dbt configuration for the specified connection.
      tags:
        - dbt
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            description: ID of the connection where dbt is configured
            example: "550e8400-e29b-41d4-a716-446655440000"
      responses:
        '200':
          description: 'dbt configuration successfully retrieved'
          content:
            application/json:
              schema:
                type: object
                required:
                  - autogenRelationships
                  - branch
                  - dbtVersion
                  - enableSemanticLayer
                  - enableVirtualSchemas
                  - projectRootPath
                  - sshUrl
                  - supportsDbt
                properties:
                  authMethod:
                    type: string
                    enum: [ssh, https_token, github_app]
                    description: |
                      The authentication method used for git operations:

                      - `ssh` - Deploy key
                      - `https_token` - Deploy token/PAT
                      - `github_app` - GitHub App installation
                    example: "ssh"
                  autogenRelationships:
                    type: boolean
                    description: Whether relationships are auto-generated from dbt
                    example: true
                  branch:
                    type: string
                    description: Git branch name
                    example: "main"
                  committerEmail:
                    type: [string, null]
                    description: Email address used for commit signing. Only returned when a commit signer is configured for GitHub App auth.
                    example: "bot@example.com"
                  committerName:
                    type: [string, null]
                    description: Name used for commit signing. Only returned when a commit signer is configured for GitHub App auth.
                    example: "Omni Bot"
                  commitSigningPublicKey:
                    type: [string, null]
                    description: SSH public key used to sign commits. Only returned when a commit signer is configured for GitHub App auth.
                    example: "ssh-ed25519 AAAA..."
                  dbtVersion:
                    type: string
                    description: dbt version being used
                    example: "Auto"
                  enableSemanticLayer:
                    type: boolean
                    description: Whether the [dbt semantic layer integration](/integrations/dbt/semantic-layer) is enabled
                    example: false
                  enableVirtualSchemas:
                    type: boolean
                    description: Whether virtual schemas are enabled
                    example: false
                  githubAppInstallationId:
                    type: [string, null]
                    description: GitHub App installation ID. Only returned when `authMethod` is `github_app`.
                    example: "12345678"
                  projectRootPath:
                    type:
                      - string
                      - "null"
                    description: Path to dbt project root
                    example: "dbt_project"
                  publicKey:
                    type: [string, null]
                    description: SSH public key for repository access (deploy key). Only returned when `authMethod` is `ssh`.
                    example: "ssh-ed25519 AAAA..."
                  sshUrl:
                    type: string
                    description: SSH URL for git repository
                    example: "git@github.com:org/repo.git"
                  supportsDbt:
                    type: boolean
                    description: |
                      Whether the connection dialect supports dbt.
                    example: true
                  message:
                    type: string
                    description: Message explaining dbt status. Returned if dbt is not configured.
                    example: "dbt not configured for this connection"
              examples:
                dbtConfigured:
                  summary: dbt is configured with SSH
                  value:
                    authMethod: "ssh"
                    autogenRelationships: true
                    branch: "main"
                    dbtVersion: "Auto"
                    enableSemanticLayer: false
                    enableVirtualSchemas: false
                    projectRootPath: "dbt_project"
                    publicKey: "ssh-ed25519 AAAA..."
                    sshUrl: "git@github.com:org/repo.git"
                    supportsDbt: true
                dbtConfiguredGitHubApp:
                  summary: dbt is configured with GitHub App
                  value:
                    authMethod: "github_app"
                    autogenRelationships: true
                    branch: "main"
                    committerEmail: "bot@example.com"
                    committerName: "Omni Bot"
                    commitSigningPublicKey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKey123"
                    dbtVersion: "Auto"
                    enableSemanticLayer: false
                    enableVirtualSchemas: false
                    githubAppInstallationId: "12345678"
                    projectRootPath: "dbt_project"
                    sshUrl: "https://github.com/org/repo.git"
                    supportsDbt: true
                dbtNotConfigured:
                  summary: dbt is not configured
                  value:
                    supportsDbt: true
                    message: "dbt not configured for this connection"

        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Permission denied - connection admin role required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Connection not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      operationId: updateDbtConfiguration
      summary: Update dbt configuration
      description: |
        <Note>
          **Connection Admin** permissions are required to use this endpoint.
        </Note>

        Update the dbt configuration for the specified connection.
      tags:
        - dbt
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            description: ID of the connection where dbt is configured
            example: "550e8400-e29b-41d4-a716-446655440000"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - autogenRelationships
                - branch
                - enableVirtualSchemas
                - sshUrl
              properties:
                authMethod:
                  type: string
                  enum: [ssh, https_token, github_app]
                  description: |
                    The authentication method to use for git operations:

                    - `ssh` - Deploy key
                    - `https_token` - Deploy token/PAT
                    - `github_app` - GitHub App installation (github.com only)

                    When omitted, the existing auth method is retained. Field/method compatibility is validated against the effective auth method (the request's `authMethod`, or the connected repo's if omitted).
                  example: "ssh"
                autogenRelationships:
                  type: boolean
                  description: Automatically generate relationships from dbt
                  example: true
                branch:
                  type: string
                  minLength: 1
                  description: Git branch name
                  example: "main"
                committerEmail:
                  type: string
                  description: |
                    **Required when `authMethod: github_app` and commit signing is enabled**. Email address used for commit signing. Must match the email the signing key is registered against in GitHub. When blank on an update, preserves the existing committer email rather than clearing it.
                  example: "bot@example.com"
                committerName:
                  type: string
                  description: |
                    **Required when `authMethod: github_app` and commit signing is enabled**. Name used for commit signing. When blank on an update, preserves the existing committer name rather than clearing it.
                  example: "Omni Bot"
                dbtVersion:
                  type:
                    - string
                    - "null"
                  enum: ["Auto", "1.11", "1.12", "2.0", null]
                  default: null
                  description: |
                    dbt version to use. Both `Auto` and `null` let Omni choose the version; `null` is the default, so omitting this field resets the version to `Auto`.

                    A version the connection's dialect can't run is rejected:

                    - `1.12` - Not supported on Databricks or ClickHouse
                    - `2.0` - Not supported on Trino, Athena, or StarRocks
                  example: "1.11"
                enableSemanticLayer:
                  type: boolean
                  default: false
                  description: Enable [dbt semantic layer integration](/integrations/dbt/semantic-layer)
                  example: false
                enableVirtualSchemas:
                  type: boolean
                  description: Enable virtual schemas from dbt
                  example: false
                githubAppInstallationId:
                  type: string
                  pattern: ^[0-9]+$
                  description: |
                    **Required when `authMethod: github_app`**. GitHub App installation ID (numeric). Updates without an installation ID keep the existing repo config for metadata-only saves, but changing the URL or switching auth methods without one is rejected.
                  example: "12345678"
                projectRootPath:
                  anyOf:
                    - type: string
                      pattern: "^(?!\\/)(?!.*\\.\\.)[\\w ./-]+$"
                    - type: string
                      enum: [""]
                    - type: "null"
                  default: null
                  description: Path to dbt project root within repository
                  example: "dbt_project"
                rotateKeys:
                  type: boolean
                  default: false
                  description: |
                    Rotate SSH deploy keys. Only applicable when `authMethod: ssh`.
                  example: false
                sshUrl:
                  type: string
                  minLength: 1
                  description: |
                    Git repository URL. Varies depending on the `authMethod`:

                    - `ssh` - A string starting with `git@...`
                    - `https_token` - A string starting with `https://...`
                    - `github_app` - A string starting with `https://github.com/...`
                  example: "git@github.com:org/repo.git"
                token:
                  type: string
                  maxLength: 1000
                  pattern: ^[a-zA-Z0-9_\-.]+$
                  description: |
                    **Required when `authMethod: https_token`**. HTTPS token for authentication (deploy token, PAT, etc.). Write-only; never returned in GET responses.
      responses:
        '200':
          description: dbt configuration updated successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - autogenRelationships
                  - branch
                  - dbtVersion
                  - enableSemanticLayer
                  - enableVirtualSchemas
                  - projectRootPath
                  - sshUrl
                  - supportsDbt
                properties:
                  authMethod:
                    type: string
                    enum: [ssh, https_token, github_app]
                    description: |
                      The authentication method used for git operations:

                      - `ssh` - Deploy key
                      - `https_token` - Deploy token/PAT
                      - `github_app` - GitHub App installation
                    example: "ssh"
                  autogenRelationships:
                    type: boolean
                    description: Whether relationships are auto-generated from dbt
                    example: true
                  branch:
                    type: string
                    description: Git branch name
                    example: "main"
                  committerEmail:
                    type: [string, null]
                    description: Email address used for commit signing. Only returned when a commit signer is configured for GitHub App auth.
                    example: "bot@example.com"
                  committerName:
                    type: [string, null]
                    description: Name used for commit signing. Only returned when a commit signer is configured for GitHub App auth.
                    example: "Omni Bot"
                  commitSigningPublicKey:
                    type: [string, null]
                    description: SSH public key used to sign commits. Only returned when a commit signer is configured for GitHub App auth.
                    example: "ssh-ed25519 AAAA..."
                  dbtVersion:
                    type: string
                    description: dbt version being used
                    example: "Auto"
                  enableSemanticLayer:
                    type: boolean
                    description: Whether the [dbt semantic layer integration](/integrations/dbt/semantic-layer) is enabled
                    example: false
                  enableVirtualSchemas:
                    type: boolean
                    description: Whether virtual schemas are enabled
                    example: false
                  githubAppInstallationId:
                    type: [string, null]
                    description: GitHub App installation ID. Only returned when `authMethod` is `github_app`.
                    example: "12345678"
                  projectRootPath:
                    type:
                      - string
                      - "null"
                    description: Path to dbt project root
                    example: "dbt_project"
                  publicKey:
                    type: [string, null]
                    description: SSH public key for repository access (deploy key). Only returned when `authMethod` is `ssh`.
                    example: "ssh-ed25519 AAAA..."
                  sshUrl:
                    type: string
                    description: Git repository URL
                    example: "git@github.com:org/repo.git"
                  supportsDbt:
                    type: boolean
                    description: Whether the connection dialect supports dbt
                    example: true
        '400':
          description: Invalid request body or validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Permission denied - connection admin role required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Connection not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      operationId: connectionsDbtDelete
      summary: Delete dbt configuration
      description: |
        <Note>
          **Connection Admin** permissions are required to use this endpoint.
        </Note>

        Remove the dbt configuration for the specified connection by removing the link to the dbt repository.
      tags:
        - dbt
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            description: ID of the connection where dbt is configured
            example: "550e8400-e29b-41d4-a716-446655440000"
      responses:
        '200':
          description: dbt configuration deleted successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - message
                  - success
                properties:
                  message:
                    type: string
                    description: Success message
                    example: "dbt repository unlinked successfully"
                  success:
                    type: boolean
                    description: Whether the operation succeeded
                    example: true
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Permission denied - Connection Admin role required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Connection not found or dbt not configured
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/connections/{connectionId}/dbt/environments:
    post:
      tags:
        - dbt
      summary: Create dbt environment
      description: |
        Create a new [dbt environment](/integrations/dbt/environments) on the specified connection. A dbt environment allows you to connect Omni to a specific dbt project and manage dbt-related functionality within the connection context.

        dbt environments can be personal (owned by a single user) or shared (owned by no one). To create:
        
        - **A personal environment for yourself**, you'll need **Restricted Querier** permissions or higher on the associated Omni model
        - **A personal environment for another user**, you'll need **Organization Admin** permissions
        - **A shared environment**, you'll need **Connection Admin** or **Organization Admin** permissions
      security:
        - bearerAuth: []
      operationId: createDbtEnvironment
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the connection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - targetSchema
              properties:
                name:
                  type: string
                  minLength: 1
                  description: The name of the dbt environment
                  example: "Production dbt"
                targetSchema:
                  type: string
                  minLength: 1
                  description: The target schema for the environment
                  example: "analytics"
                isDeferralEnabled:
                  type: boolean
                  default: false
                  description: |
                    Whether deferral is enabled for this environment. Defaults to `false`. Ignored (forced to `false`) for the default (production) environment.
                  example: false
                ownerId:
                  type: string
                  format: uuid
                  nullable: true
                  description: |
                    Defines the owner of the environment, which also determines the type of environment (personal or shared) that is created.

                    - **Personal environment** - Use the organization membership ID of the user that owns the environment, not their user ID. **Organization Admin** permissions are required to set the ID of a user other than the authenticating user. Get your own membership ID from the [Who Am I API](/api/who-am-i/get-current-identity-and-permissions), or another user's from the [Get model roles API](/api/user-model-roles/retrieve-user-model-roles).
                    - **Shared environment** - Set to `null`. Requires **Connection Admin** or **Organization Admin** permissions.
                  example: "550e8400-e29b-41d4-a716-446655440000"
                targetDatabase:
                  type: string
                  nullable: true
                  description: Target database override
                  example: "analytics_db"
                targetName:
                  type: string
                  nullable: true
                  description: Target name override
                  example: "my_project"
                targetRole:
                  type: string
                  nullable: true
                  description: Target role override
                  example: "transformer"
                variables:
                  type: array
                  default: []
                  description: |
                    Environment variables for the dbt environment. **Note**: While `variables` is not required, at least one variable object must be defined if `variables` is included in the request body.
                  items:
                    type: object
                    required:
                      - name
                      - value
                      - isSecret
                    properties:
                      name:
                        type: string
                        minLength: 1
                        description: Variable name
                        example: "DBT_TARGET"
                      value:
                        type: string
                        description: Variable value. May be empty, except for secret variables.
                        example: "production"
                      isSecret:
                        type: boolean
                        description: Whether the variable value is secret
                        example: false
            examples:
              basic:
                summary: Basic dbt environment
                value:
                  name: "Production dbt"
                  targetSchema: "analytics"
              withOwner:
                summary: dbt environment with specific owner
                value:
                  name: "Production dbt"
                  targetSchema: "analytics"
                  ownerId: "550e8400-e29b-41d4-a716-446655440000"
              withVariables:
                summary: dbt environment with variables
                value:
                  name: "Production dbt"
                  targetSchema: "analytics"
                  targetDatabase: "analytics_db"
                  variables:
                    - name: "DBT_TARGET"
                      value: "production"
                      isSecret: false
                    - name: "DBT_API_KEY"
                      value: "sk-..."
                      isSecret: true
      responses:
        '201':
          description: dbt environment created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DbtEnvironment'
              example:
                id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                name: "Production dbt"
                isDefaultEnvironment: false
                isDeferralEnabled: false
                ownerId: "550e8400-e29b-41d4-a716-446655440000"
                targetDatabase: "analytics_db"
                targetName: null
                targetRole: null
                targetSchema: "analytics"
                variables:
                  - id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    name: "DBT_TARGET"
                    value: "production"
                    isSecret: false
                  - id: "c3d4e5f6-a7b8-9012-cdef-123456789012"
                    name: "DBT_API_KEY"
                    value: null
                    isSecret: true
        '400':
          description: |
            Bad Request

            Possible error messages:
            - Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible causes:
            - Insufficient permissions. Creating a shared environment requires **Connection Admin**; creating an environment owned by another user requires **Organization Admin**.
            - Non-admin user attempting to set `ownerId` for another user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Connection or owner not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User with id 9e8719d9-276a-4964-9395-a493189a247c does not exist"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - dbt
      summary: List dbt environments
      description: |
        <Note>
          This endpoint requires **Restricted Querier** permissions or higher on the connection.
        </Note>
        
        Retrieves a list of all dbt environments configured for the specified connection.
      security:
        - bearerAuth: []
      operationId: listDbtEnvironments
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the connection
      responses:
        '200':
          description: List of dbt environments retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DbtEnvironment'
              examples:
                multipleEnvironments:
                  summary: Multiple environments
                  value:
                    - id: "247dc6dc-2a58-4688-9521-c5ed3e99c1e8"
                      name: "Production"
                      isDefaultEnvironment: true
                      isDeferralEnabled: false
                      ownerId: null
                      targetDatabase: null
                      targetName: null
                      targetRole: null
                      targetSchema: "public"
                      variables: []
                    - id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                      name: "Development"
                      isDefaultEnvironment: false
                      isDeferralEnabled: true
                      ownerId: "550e8400-e29b-41d4-a716-446655440000"
                      targetDatabase: "dev_analytics_db"
                      targetName: null
                      targetRole: null
                      targetSchema: "analytics_dev"
                      variables:
                        - id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                          name: "DBT_TARGET"
                          value: "dev"
                          isSecret: false
                        - id: "c3d4e5f6-a7b8-9012-cdef-123456789012"
                          name: "DBT_API_KEY"
                          value: null
                          isSecret: true
                emptyList:
                  summary: No environments configured
                  value: []
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible causes:
            - Insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Connection not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    
  /v1/connections/{connectionId}/dbt/environments/{environmentId}:
    put:
      tags:
        - dbt
      summary: Update dbt environment
      description: |
        <Note>
          The permissions required depend on who owns the environment:

          - **Your own personal environment** - **Restricted Querier** permissions or higher on the connection
          - **A shared environment** - **Connection Admin** or **Organization Admin** permissions
          - **Another user's personal environment** - **Organization Admin** permissions
        </Note>
        
        Update an existing dbt environment on the specified connection. This request replaces the environment: `name` and `targetSchema` are required, and any omitted optional field is reset to its default. In particular, omitting `ownerId` sets it to `null`, which converts the environment to a shared one and requires **Connection Admin** permissions - send your own membership ID when updating your own environment.
      security:
        - bearerAuth: []
      operationId: updateDbtEnvironment
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the connection
        - name: environmentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the dbt environment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - targetSchema
              properties:
                name:
                  type: string
                  minLength: 1
                  description: The name of the dbt environment
                  example: "Development"
                targetSchema:
                  type: string
                  minLength: 1
                  description: The target schema for the environment
                  example: "dev_schema"
                isDeferralEnabled:
                  type: boolean
                  description: |
                    Whether deferral is enabled for this environment. Ignored (forced to `false`) for the default (production) environment.
                  example: true
                ownerId:
                  type: string
                  format: uuid
                  nullable: true
                  description: |
                    Defines the owner of the environment, which also determines whether the environment is personal or shared. Defaults to `null` when omitted, so always send the current owner's ID unless you intend to change it.

                    - **Personal environment** - Use the organization membership ID of the user that owns the environment, not their user ID. **Organization Admin** permissions are required to set the ID of a user other than the authenticating user. Get your own membership ID from the [Who Am I API](/api/who-am-i/get-current-identity-and-permissions), or another user's from the [Get model roles API](/api/user-model-roles/retrieve-user-model-roles).
                    - **Shared environment** - Set to `null`. Requires **Connection Admin** or **Organization Admin** permissions.
                  example: "550e8400-e29b-41d4-a716-446655440000"
                targetDatabase:
                  type: string
                  nullable: true
                  description: Target database override
                  example: "analytics_dev"
                targetName:
                  type: string
                  nullable: true
                  description: Target name override
                  example: "custom_target"
                targetRole:
                  type: string
                  nullable: true
                  description: Target role override
                  example: "developer"
                variables:
                  type: array
                  description: |
                    Environment variables for the dbt environment. To update existing variables, include the variable `id` field. New variables should omit the `id` field.
                  items:
                    type: object
                    required:
                      - name
                      - value
                      - isSecret
                    properties:
                      id:
                        type: string
                        format: uuid
                        description: Variable identifier. Required for updating existing variables, otherwise omit.
                        example: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                      name:
                        type: string
                        minLength: 1
                        description: Variable name. Required for new variables, otherwise omit.
                        example: "DBT_TARGET"
                      value:
                        type: string
                        description: |
                          Variable value. Required when creating a variable. It can be an empty string unless `isSecret` is `true`.

                          When updating an existing variable, omit this field or send `null` to keep the current value. An empty string clears a non-secret variable's value but keeps a secret variable's current value.
                        example: "production"
                      isSecret:
                        type: boolean
                        description: Whether the variable value is secret. Required for new and updating existing variables.
                        example: false
            examples:
              basic:
                summary: Update name and schema
                value:
                  name: "Development"
                  targetSchema: "dev_schema"
              withOwner:
                summary: Update ownership
                value:
                  name: "Production"
                  ownerId: "550e8400-e29b-41d4-a716-446655440000"
              withVariables:
                summary: Update with variables
                value:
                  name: "Production"
                  targetSchema: "dev_schema"
                  variables:
                    - id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                      name: "DBT_TARGET"
                      value: "staging"
                      isSecret: false
                    - name: "NEW_VAR"
                      value: "new_value"
                      isSecret: false
      responses:
        '200':
          description: dbt environment updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DbtEnvironment'
              example:
                id: "247dc6dc-2a58-4688-9521-c5ed3e99c1e8"
                name: "Development"
                isDefaultEnvironment: false
                isDeferralEnabled: true
                ownerId: 550e8400-e29b-41d4-a716-446655440000
                targetDatabase: "analytics_dev"
                targetName: "custom_target"
                targetRole: "developer"
                targetSchema: "dev_schema"
                variables:
                  id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                  name: "DBT_TARGET"
                  value: "staging"
                  isSecret: false
        '400':
          description: |
            Bad Request

            Possible error messages:
            - Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible causes:
            - Insufficient permissions. Only the environment's owner, a **Connection Admin** (shared environments), or an **Organization Admin** can update it.
            - Non-admin user attempting to set `ownerId` to `null` or to another user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Connection, environment, or owner not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User with id 9e8719d9-276a-4964-9395-a493189a247c does not exist"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - dbt
      summary: Delete dbt environment
      description: |
        <Note>
          The permissions required depend on who owns the environment:

          - **Your own personal environment** - **Restricted Querier** permissions or higher on the connection
          - **A shared environment** - **Connection Admin** or **Organization Admin** permissions
          - **Another user's personal environment** - **Organization Admin** permissions
        </Note>

        Delete an existing dbt environment from the specified connection. This action cannot be undone.
      security:
        - bearerAuth: []
      operationId: deleteDbtEnvironment
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the connection
        - name: environmentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the dbt environment to delete
      responses:
        '200':
          description: dbt environment deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible causes:
            - Insufficient permissions. Only the environment's owner, a **Connection Admin** (shared environments), or an **Organization Admin** can delete it.
            - Connection dialect does not support dbt environments
            - Attempting to access a resource in a different organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Connection or dbt environment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents:
    post: 
      tags:
        - Documents
      summary: Create document
      deprecated: true
      description: |
        <Warning>
          This endpoint is deprecated. Use the [Documents v2 API](/api/documents-v2/create-document) instead.
        </Warning>

        Create a new document
      security:
        - bearerAuth: []
      operationId: createDocument
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - modelId
                - name
              properties:
                modelId:
                  type: string
                  format: uuid
                  description: The ID of the model to build the document on
                name:
                  type: string
                  description: The name of the document
                description:
                  type: string
                  minLength: 1
                  maxLength: 1024
                  description: Description of the document
                identifier:
                  type: string
                  description: |
                    Custom identifier for the document. If not provided, an identifier will be auto-generated.

                    The identifier is used in the document's URL and must be unique across all documents in your organization.

                    **Format requirements:**
                    
                    - Must match the pattern: lowercase letters, numbers, hyphens, and underscores only
                    - Cannot start or end with a hyphen or underscore

                    **Note:** Once created, the identifier can be changed using the [Rename document identifier endpoint](/api/documents-v2/rename-document-identifier).
                  example: "sales-dashboard-2026"
                queryPresentations:
                  type: array
                  description: An array of query presentation objects, each representing a query in the document's workbook
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                        description: The name of the query
                      description:
                        type: string
                        description: A description of the query
                      chartType:
                        type: string
                        nullable: true
                        enum:
                          - 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
                        description: Chart type for the query's visualization
                      query:
                        type: object
                        description: The query definition object
                      queryIdentifierMapKey:
                        type: string
                        description: Accepted for compatibility with exported query presentations. Ignored during creation — the document assigns new query keys.
                      visConfig:
                        type: object
                        description: Visualization configuration for the query
            examples:
              basicDocument:
                summary: Create document with auto-generated identifier
                value:
                  modelId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  name: "Sales Dashboard"
                  description: "Q1 2026 sales performance metrics"
              customIdentifier:
                summary: Create document with custom identifier
                value:
                  modelId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  name: "Sales Dashboard"
                  identifier: "sales-dashboard-2026"
                  description: "Q1 2026 sales performance metrics"
      responses:
        '200':
          description: Document created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  dashboard:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        description: The dashboard ID
                      createdAt:
                        type: string
                        format: date-time
                        description: Timestamp when the dashboard was created
                      updatedAt:
                        type: string
                        format: date-time
                        description: Timestamp when the dashboard was last updated
                      metadata:
                        type: object
                        description: Dashboard metadata including layouts and tile settings
                        properties:
                          layouts:
                            type: object
                            description: Layout configurations for different screen sizes
                          textTiles:
                            type: array
                            items:
                              type: object
                          hiddenTiles:
                            type: array
                            items:
                              type: string
                          tileSettings:
                            type: object
                          tileFilterMap:
                            type: object
                          tileControlMap:
                            type: object
                      metadataVersion:
                        type: integer
                        description: Version of the metadata schema
                      refreshInterval:
                        type: integer
                        description: Auto-refresh interval in seconds
                      facetFilters:
                        type: boolean
                        description: Whether facet filters are enabled
                      organizationId:
                        type: string
                        format: uuid
                        description: The organization ID
                      workbookId:
                        type: string
                        format: uuid
                        description: The associated workbook ID
                      creatorId:
                        type: string
                        format: uuid
                        description: The ID of the user who created the dashboard
                      updaterId:
                        type: string
                        format: uuid
                        description: The ID of the user who last updated the dashboard
                      queryPresentationCollectionId:
                        type: string
                        format: uuid
                        description: The query presentation collection ID
                      dashboardId:
                        type: string
                        format: uuid
                        description: The dashboard ID (same as id)
                  workbook:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        description: The workbook ID
                      createdAt:
                        type: string
                        format: date-time
                        description: Timestamp when the workbook was created
                      updatedAt:
                        type: string
                        format: date-time
                        description: Timestamp when the workbook was last updated
                      deletedAt:
                        type: string
                        format: date-time
                        description: Timestamp when the workbook was deleted, if applicable
                      publishedAt:
                        type: string
                        format: date-time
                        description: Timestamp when the workbook was published
                      isDraft:
                        type: boolean
                        description: Whether the workbook is a draft
                      identifier:
                        type: string
                        description: The unique identifier for the document
                      name:
                        type: string
                        description: The name of the document
                      lastItemIndex:
                        type: integer
                        description: Index of the last item in the workbook
                      ephemeral:
                        type: string
                        description: Ephemeral state identifier
                      organizationRole:
                        type: string
                        description: Organization-level role for the document
                      organizationAccessBoost:
                        type: boolean
                      publicRole:
                        type: string
                      publicAccessBoost:
                        type: boolean
                      canAnalyze:
                        type: boolean
                        description: Whether users can analyze data in the document
                      canDownload:
                        type: boolean
                        description: Whether users can download data from the document
                      canDrill:
                        type: boolean
                        description: Whether users can drill into data in the document
                      canSchedule:
                        type: boolean
                        description: Whether users can create schedules in the document
                      canUpload:
                        type: boolean
                        description: Whether users can upload data in the document's workbook
                      canViewWorkbook:
                        type: boolean
                        description: Whether users can view the workbook in the document
                      canUseTimezoneOverride:
                        type: boolean
                        description: Whether users can use the [timezone picker](/visualize-present/dashboards/timezone-override) to change the query timezone for this document
                      organizationId:
                        type: string
                        format: uuid
                        description: The organization ID
                      ownerId:
                        type: string
                        format: uuid
                        description: The ID of the document owner
                      updaterId:
                        type: string
                        format: uuid
                        description: The ID of the user who last updated the document
                      folderId:
                        type: string
                        format: uuid
                        description: The folder ID, if the document is in a folder
                      originDocumentId:
                        type: string
                        format: uuid
                        description: The origin document ID, if duplicated
                      documentId:
                        type: string
                        format: uuid
                        description: The document ID
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `modelId: Required`
            - `name: Required`
            - Identifier format is invalid (must be lowercase letters, numbers, hyphens, and underscores; cannot start/end with hyphen or underscore)
            - Identifier is already in use by another document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - Documents
      summary: List documents
      description: List documents with pagination and filtering
      security:
        - bearerAuth: []
      operationId: listDocuments
      parameters:
        - name: include
          in: query
          schema:
            type: string
          description: |
            Comma-separated list of additional fields to include in the response:
            - `_count` - Adds favorite and view count metrics
            - `labels` - Includes associated document labels
            - `includeDeleted` - Include deleted documents
            - `onlyFavorites` - **Must be used with `userId`.** Include only documents that the specified user has favorited.
            - `onlySharedWithMe` - Returns only documents explicitly shared with the specified user via documents permissions. Excludes documents owned by the user. Cannot be combined with `onlyFavorites` or `folderId`.

               Additionally, depending on the type of API key being used:

               - **Organization API keys** require the `userId` parameter
               - **Personal Access Tokens** automatically infer the `userId` from the token
        - name: labels
          in: query
          schema:
            type: string
          description: |
            Comma-separated list of labels to filter results. For example: `finance,marketing`
        - name: folderId
          in: query
          schema:
            type: string
            format: uuid
          description: ID of the folder to filter results. Returns only documents within the specified folder.
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Number of records per page
        - name: sortField
          in: query
          schema:
            type: string
            enum: [favorites, lastViewedAt, name, updatedAt, visits]
            default: name
          description: |
            Field to sort by:
            - `favorites` - Sort by the number of favorites
            - `lastViewedAt` - Sort by when the document's dashboard was last viewed
            - `name` - Sort by document name
            - `updatedAt` - Sort by last update time
            - `visits` - Sort by view count
        - name: sortDirection
          in: query
          schema:
            type: string
            enum: [asc, desc]
            default: desc
          description: Sort direction (`asc` for ascending, `desc` for descending)
        - name: cursor
          in: query
          schema:
            type: string
          description: Cursor for pagination. Used with `sortField`/`sortDirection` for relative positioning.
        - name: userId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            The ID of a standard or embed user to filter results, returning only documents the specified user can view based on their permissions.

            If `include=onlyFavorites` is specified:

            - **And using an Organization API key**, this parameter is **required**
            - **And using a Personal Access Token**, the `userId` will be automatically inferred from the token
        - name: creatorId
          in: query
          schema:
            type: string
            format: uuid
          description: ID of the user who created the document(s), returning only documents that the specified user created.
      responses:
        '200':
          description: Paginated document list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsListResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `pageSize: Page size must be at least 1`
            - `pageSize: Page size cannot exceed 100`
            - `sortField: Invalid enum value. Expected 'favorites' | 'lastViewedAt' | 'name' | 'updatedAt' | 'visits', received '<invalidField>'`
            - `creatorId: Invalid uuid`
            - `userId: Invalid uuid`
            - `formErrors: Unrecognized key(s) in object: '<unknownParameter>'`
            - `onlySharedWithMe requires userId` - When using `include=onlySharedWithMe` without providing a `userId` parameter
            - `onlySharedWithMe cannot be combined with onlyFavorites` - When using both `include=onlySharedWithMe` and `include=onlyFavorites` together
            - `onlySharedWithMe cannot be combined with folderId` - When using `include=onlySharedWithMe` with a `folderId` parameter
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:
            - `User with id <uuid> does not exist`

            **Note**: An invalid UUID format will result in a 400 error. This 404 error occurs when the provided ID is a valid UUID format but the user cannot be found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/move:
    put:
      tags:
        - Documents
      summary: Move document
      description: |
        Move a document to new folder or change scope.
      security:
        - bearerAuth: []
      operationId: moveDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The ID of the document to move. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - folderPath
              properties:
                folderPath:
                  oneOf:
                    - type: string
                    - type: "null"
                  description: The path of the destination folder. Use `null` to move the document to the root level (no folder).
                scope:
                  type: string
                  enum: [organization, restricted]
                  description: |
                    Optional sharing scope for the document:
                    - `organization` - Organization-wide access
                    - `restricted` - Limited access

                    If not provided, the scope will be computed based on the document or the destination folder.

                    **Note**: When providing a scope, it must match the destination folder's scope.
      responses:
        '200':
          description: Document moved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Scope "<scope>" and folder scope "<folderScope>" do not match`
            - `Invalid method`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages/causes:

            - `User-scoped API keys cannot act on behalf of other users`
            - Operations targeting personal/restricted scope require the user's **Personal content access** setting (`omni_allows_personal_content` system attribute) to be enabled for the user. If not enabled, the API will respond with `403 Forbidden`.
        '404':
          description: |
            Not Found

            Possible error messages:
            - `Document with identifier "<documentId>" not found`
            - `Folder with path <path> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/duplicate:
    post:
      tags:
        - Documents
      summary: Duplicate published document
      description: Duplicate a published document.
      x-mint:
        content: |
          <Warning>
            Only published documents can be duplicated. Attempting to duplicate a draft document will return a 404 error.
          </Warning>
      security:
        - bearerAuth: []
      operationId: duplicateDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: Document identifier
        - name: userId
          in: query
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Optional user ID that attributes the action to the specified user.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 255
                  description: The name for the duplicated document
                folderPath:
                  oneOf:
                    - type: string
                    - type: "null"
                  description: Path to the destination folder. If not provided, the document will be duplicated to the root.
                scope:
                  type: string
                  enum: [organization, restricted]
                  description: |
                    The visibility scope for the duplicated document.

                    If not provided, inherits from the destination folder or defaults to `restricted`.
            example:
              name: "My Dashboard Copy"
              folderPath: "/reports/2024/q4"
              scope: "organization"
      responses:
        '201':
          description: Document duplicated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  dashboardId:
                    type: string
                    description: The dashboard ID, if the duplicated document has a dashboard
                  identifier:
                    type: string
                    description: Unique identifier for the newly created document
                  name:
                    type: string
                    description: The name of the duplicated document
                  workbookId:
                    type: string
                    description: The workbook ID of the duplicated document
              example:
                dashboardId: "abc123-dash-id"
                identifier: "xyz789-new-identifier"
                name: "My Dashboard Copy"
                workbookId: "def456-workbook-id"
        '400':
          description: |
            Bad Request. Possible error messages:

            - `name: Name cannot be empty`
            - `name: Name must be 255 characters or less`
            - `Scope "organization" and folder scope "restricted" do not match`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Insufficient permissions. Possible error messages/causes:

            - `User does not have access to create content on the specified connection`
            - `User-scoped API keys can only be used to act on behalf of the authenticated user.`
            - `Document duplication is disabled`. The document's duplication permission is disabled.
            - Operations targeting personal/restricted scope require the user's **Personal content access** setting (`omni_allows_personal_content` system attribute) to be enabled for the user. If not enabled, the API will respond with `403 Forbidden`.
            - `You do not have permission to add documents to the specified folder.` Editor permissions or higher are required on the destination folder. Does not apply to Organization Admins.
            - `You do not have permission to add content to the shared root.` No `folderId` was given, the scope is `organization`, and the organization's setting for member write access to the shared root is off. Does not apply to Organization Admins.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible error messages:

            - `Document with identifier "<documentId>" not found`
            - `Published document with id "<documentId>" does not exist`. This will surface when attempting to duplicate a draft.
            - `Folder with path "<folderPath>" does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/upgrade:
    post:
      tags:
        - Documents
      summary: Upgrade dashboard layout
      description: |
        Upgrade a document to the advanced dashboard layout — the same action available in the UI under **File > Upgrade layout**.

        <Note>
          This operation is idempotent. If the document already uses the advanced layout, the request is a no-op and returns `upgraded: false`.
        </Note>

        For published documents the upgrade runs through a draft-and-publish workflow automatically. If a draft already exists, the request returns `409` unless `clearExistingDraft` is set to `true`, which discards the existing draft before upgrading.
      operationId: documentsUpgradeLayout
      parameters:
        - name: identifier
          in: path
          required: true
          schema:
            type: string
          description: Document identifier (either document ID or identifier slug)
          example: abc123
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentsUpgradeLayoutBody'
            example:
              clearExistingDraft: true
      responses:
        '200':
          description: Layout upgraded, or no-op if the document already had advanced layout.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsUpgradeLayoutResponse'
              example:
                identifier: abc123
                upgraded: true
        '400':
          description: |
            Bad Request

            - The document does not have a dashboard to upgrade.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Permission denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '409':
          description: A draft already exists for the published document; set `clearExistingDraft` to `true` to override.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/transfer-ownership:
    put:
      tags:
        - Documents
      summary: Transfer document ownership
      deprecated: true
      description: |
        <Warning>
          This endpoint is deprecated. Use the [Grant document permissions](/api/document-permissions/grant-document-permissions) and [Update document permissions](/api/document-permissions/update-document-permissions) endpoints to assign document ownership.
        </Warning>

        Transfer ownership of a document to another user in the organization.

        To successfully transfer a document:

        - The user performing the transfer must have `MANAGER` permissions or higher on the document
        - The new owner must be a member of the organization AND have explicit access to the document

        Upon success, the following occurs:

        - The named user is granted `OWNER` role on the document
        - All other users with `OWNER` role are demoted to `MANAGER` permissions on the document
        - Explicit permits for the new owner are revoked, as they now have owner access
        - Content search index is updated

        **Note:** This operation does not modify the document's creator, and does not move a restricted document out of its current folder. Transferring to the current owner is a no-op and returns success.
      security:
        - bearerAuth: []
      operationId: transferDocumentOwnership
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The ID of the document to transfer. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
              properties:
                userId:
                  type: string
                  description: The membership ID of the user to transfer ownership to. This must be a valid organization member who has explicit permission on the document.
            example:
              userId: "9e8719d9-276a-4964-9395-a493189a247c"
      responses:
        '200':
          description: |
            Document ownership transferred successfully.

            **Note:** This response is also returned when transferring to the current owner.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
              example:
                success: true
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Invalid JSON`
            - `userId is required`
            - `New owner must have explicit document permission`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                invalidJson:
                  summary: Invalid JSON body
                  value:
                    error: "Invalid JSON"
                missingUserId:
                  summary: Missing userId field
                  value:
                    error: "userId is required"
                noPermission:
                  summary: New owner lacks document permission
                  value:
                    error: "New owner must have explicit document permission"
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Insufficient permissions` - Acting user does not have `MANAGER` or higher permissions on the document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: "Insufficient permissions"
        '404':
          description: |
            Not Found

            Possible error messages:
            
            - `Document with identifier "<documentId>" not found`
            - `User not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                documentNotFound:
                  summary: Document not found
                  value:
                    error: "Document with identifier \"doc-123\" not found"
                userNotFound:
                  summary: User not found
                  value:
                    error: "User not found"
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/draft:
    post:
      tags:
        - Documents
      summary: Create draft
      deprecated: true
      description: |
        <Warning>
          This endpoint is deprecated. Use the [Documents v2 API](/api/documents-v2/create-draft-and-patch-document) instead.
        </Warning>

        <Note>
          This endpoint requires **Editor** permissions or higher on the specified document.
        </Note>

        Create a [draft](/content/develop/drafts) of the specified document. Drafts are isolated copies of a document - including its workbook and dashboard - that can be modified without impacting the published version.

        To create a draft on a [branch](/content/develop/branch-mode), include a `branchId` in the request body. Each branch can have one draft per document.

        If a draft already exists for the document (or branch, if specified), the existing draft is returned instead of creating a new one.
      security:
        - bearerAuth: []
      operationId: createDraft
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The ID of the document to create a draft for. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                branchId:
                  type: string
                  format: uuid
                  description: The UUID of the branch to create the draft on. Required when creating a draft on a branch. To retrieve branch IDs, use the [List models](/api/models/list-models) endpoint with `modelKind=BRANCH`.
      responses:
        '200':
          description: Draft created successfully (or existing draft returned)
          content:
            application/json:
              schema:
                type: object
                properties:
                  identifier:
                    type: string
                    description: "Identifier of the new draft. If a draft already exists for the document or branch specified, this will be the identifier of the existing draft."
                    example: "93874a87"
        '400':
          description: |
            Bad request

            Possible error messages:
            - Document is not eligible for publishing workflow
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:
            - Permission denied - EDITOR role required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:
            - `Document with identifier <documentId> not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

    delete:
      tags:
        - Documents
      summary: Archive draft
      description: |
        Archive the current draft of the specified document. Archived drafts are placed in the [**Archived** section of the **Drafts** drawer](/content/develop/drafts#archiving-drafts) in the document's workbook and retained for 30 days.

        If a branch is specified, the current draft on the branch is archived.
      security:
        - bearerAuth: []
      operationId: archiveDraft
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The ID of the document to archive the draft from. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                branchId:
                  type: string
                  format: uuid
                  description: The UUID of the branch the draft is attached to. To retrieve branch IDs, use the [List models](/api/models/list-models) endpoint with `modelKind=BRANCH`.
      responses:
        '200':
          description: Draft archived successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: "Draft deleted successfully"
        '404':
          description: |
            Not Found

            Possible error messages:
            - `Document with identifier <documentId> not found`
            - `Draft with id <documentId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/drafts:
    get:
      tags:
        - Documents
      summary: List document drafts
      description: |
        <Note>
          This endpoint requires **Viewer** permissions or higher on the specified document.
        </Note>

        List all drafts attached to a published document. A document in Omni can have up to one draft per branch plus one draft not attached to a branch.
        
        By default, returns active drafts only. Pass `?include=archived` to include soft-deleted drafts retained for approximately 7 days.
      security:
        - bearerAuth: []
      operationId: listDocumentDrafts
      parameters:
        - name: identifier
          in: path
          required: true
          schema:
            type: string
          description: |
            The identifier of the published document. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
        - name: include
          in: query
          required: false
          schema:
            type: string
            enum:
              - archived
          description: |
            Pass `archived` to include soft-deleted drafts in the response. By default, only active drafts are returned.
          example: archived
      responses:
        '200':
          description: |
            List of drafts retrieved successfully. Results are sorted in descending order using the draft's `updatedAt`.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    identifier:
                      type: string
                      description: The identifier of the draft.
                      example: "d56b7f56"
                    publishedIdentifier:
                      type: string
                      description: The identifier of the published document this draft is attached to.
                      example: "ecd01fe5"
                    workbookModelId:
                      type: string
                      format: uuid
                      description: The UUID of the workbook model.
                      example: "d61034e2-05fe-4268-a3a7-d4cdb8d650e9"
                    branch:
                      type: object
                      nullable: true
                      description: The branch this draft is attached to, or `null` for draft not attached to a branch. A document can only have one draft not attached to a branch.
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: The UUID of the branch.
                        name:
                          type: string
                          nullable: true
                          description: The name of the branch. Can be `null` if the branch is unnamed.
                      example:
                        id: "a9bc51d2-1234-5678-9abc-def012345678"
                        name: "governance/relabel-age-average"
                    status:
                      type: string
                      enum:
                        - active
                        - archived
                      description: The status of the draft.
                      example: "active"
                    draftOutOfDate:
                      type: boolean
                      description: Indicates whether the draft is [out of date](/content/develop/branch-mode#working-with-out-of-date-drafts) compared to the published document.
                      example: false
                    createdAt:
                      type: string
                      format: date-time
                      description: Timestamp when the draft was created.
                      example: "2026-05-06T13:44:00.000Z"
                    updatedAt:
                      type: string
                      format: date-time
                      description: Timestamp when the draft was last updated.
                      example: "2026-05-06T13:44:26.000Z"
                    createdBy:
                      type: object
                      description: User who created the draft.
                      properties:
                        name:
                          type: string
                          description: Display name of the user who created the draft.
                      example:
                        name: "Blob Ross"
                    lastEditedBy:
                      type: object
                      description: User who last edited the draft.
                      properties:
                        name:
                          type: string
                          description: Display name of the user who last edited the draft.
                      example:
                        name: "Blob Ross"
              example:
                - identifier: "d56b7f56"
                  publishedIdentifier: "ecd01fe5"
                  workbookModelId: "d61034e2-05fe-4268-a3a7-d4cdb8d650e9"
                  branch:
                    id: "a9bc51d2-1234-5678-9abc-def012345678"
                    name: "governance/relabel-age-average"
                  status: "active"
                  draftOutOfDate: false
                  createdAt: "2026-05-06T13:44:00.000Z"
                  updatedAt: "2026-05-06T13:44:26.000Z"
                  createdBy:
                    name: "Blob Ross"
                  lastEditedBy:
                    name: "Blob Ross"
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Invalid include parameter value`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:
            - `You do not have permission to view this document.` - Authenticating user must have **Viewer** permissions or higher on the document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:
            - `Document with identifier <identifier> not found`
            - `Published document with id <identifier> does not exist` - Document exists but has not been published
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/queries:
    get:
      tags:
        - Documents
      summary: List document queries
      description: |
        Retrieve queries associated with a document by its identifier. The specified document must be a saved document with at least one query in the workbook.

        An organization API key can pass `userId` to list the queries as that member instead of the key's creator — the member's access to the document decides what's returned. A user-scoped key can only ever act as itself.
      x-mint:
        content: |
          <Tip>
            Check out the [Running document queries with APIs guide](/guides/api/run-document-queries) to learn how to programmatically run the queries you retrieve with this API.
          </Tip>
      security:
        - bearerAuth: []
      operationId: documentsGetQueries
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The unique identifier of the document. This can be retrieved by opening **File > Document settings** and clicking **Settings**. The **Identifier** field contains the document ID.
          example: abc123
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: |
            Every saved query in the document, in tab order. 
            
            Note that when a document has a presentation (dashboard or app), the layout decides which queries are rendered. This means that this response may include queries that aren't included in a layout.
          content:
            application/json:
              schema:
                type: object
                required:
                  - queries
                properties:
                  queries:
                    type: array
                    description: List of queries in the document
                    items:
                      type: object
                      required:
                        - id
                        - name
                        - queryIdentifierMapKey
                        - url 
                      properties:
                        description:
                          type: string
                          description: Query description; omitted when not set
                        id:
                          type: string
                          description: Query presentation ID
                        name:
                          type: string
                          description: Query presentation name
                        query:
                          description: Query JSON definition
                        queryIdentifierMapKey:
                          type: string
                          description: Key in the query identifier map
                        subTitle:
                          type: string
                          description: Query subtitle; omitted when not set
                        url:
                          type: string
                          description: URL to view this specific query/sheet in the workbook.
                          example: https://org.omni.co/w/abc123?key=1
              example:
                queries:
                  - id: "cd54494d-318f-4105-af1c-10af59e62b2e"
                    name: "Revenue by region"
                    description: "Net of refunds"
                    query:
                      fields:
                        - "order_items.total_revenue"
                        - "orders.region"
                      table: "order_items"
                      limit: 1000
                      filters: {}
                      sorts: []
                      modelId: "abc123de-f456-7890-abcd-ef1234567890"
                    queryIdentifierMapKey: "1"
                    url: "https://org.omni.co/w/abc123?key=1"
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `userId: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:
            - `User cannot view document`

            Also returned when a user-scoped key passes a `userId` naming a different member, or when `userId` names an embed user with no live embed session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Document with id <identifier> does not exist`
            - The identifier names an unsaved analysis, which has no queries to retrieve
            - `userId` does not name a member of the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/queries/{queryId}/run:
    post:
      tags:
        - Documents
      summary: Run a saved document query
      description: |
        Run one of a document's saved queries and returns the results as JSON, CSV, or XLSX. Only adding and updating existing filters is supported. To modify more than a query's filters before running it, see the [Running document queries with APIs guide](/guides/api/run-document-queries).

        View access is required to run queries that are shown as tiles on a dashboard, or any saved query on a document with an app. All other saved queries require access to the document's workbook.
        
        This API is the only way to run a dashboard tile for a user with read-only access to a dashboard. To run a query definition you supply yourself, use the [Run query API](/api/queries) instead.
      security:
        - bearerAuth: []
      operationId: documentsRunQuery
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: Document identifier (either document ID or identifier slug)
          example: abc123
        - name: queryId
          in: path
          required: true
          schema:
            type: string
            minLength: 1
          description: 'The saved query to run: its queryIdentifierMapKey, or its query presentation id, from [GET /v1/documents/{documentId}/queries](/api/documents/list-document-queries).'
          example: '1'
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: false
        content:
          application/json:
            schema:  # DocumentsRunQueryBody
              type: object
              properties:
                filters:
                  type: object
                  additionalProperties:
                    anyOf:
                      - type: string
                      - type: number
                      - type: boolean
                      - type: 'null'
                      - type: array
                        items:
                          anyOf:
                            - type: string
                            - type: number
                            - type: boolean
                            - type: 'null'
                      - type: object
                        properties:
                          cancel_query_filter:
                            type: boolean
                          exclude_from_bind_all:
                            type: boolean
                          ignore_if_unjoinable:
                            type: boolean
                          appliedLabels:
                            type: object
                            additionalProperties:
                              type: string
                          case_insensitive:
                            type: boolean
                          is_negative:
                            type: boolean
                            nullable: true
                          kind:
                            type: string
                            enum:
                              - CONTAINS
                              - ENDS_WITH
                              - STARTS_WITH
                              - EQUALS
                              - IS_EMPTY
                              - SQL_LIKE
                          type:
                            type: string
                            enum:
                              - string
                          values:
                            type: array
                            items:
                              type: string
                        required:
                          - kind
                          - type
                          - values
                      - type: object
                        properties:
                          cancel_query_filter:
                            type: boolean
                          exclude_from_bind_all:
                            type: boolean
                          ignore_if_unjoinable:
                            type: boolean
                          is_inclusive:
                            type: boolean
                          is_negative:
                            type: boolean
                            nullable: true
                          kind:
                            type: string
                            enum:
                              - LESS_THAN
                              - GREATER_THAN
                              - EQUALS
                              - BETWEEN
                          type:
                            type: string
                            enum:
                              - number
                          values:
                            type: array
                            items:
                              anyOf:
                                - type: string
                                - type: number
                        required:
                          - kind
                          - type
                          - values
                      - type: object
                        properties:
                          cancel_query_filter:
                            type: boolean
                          exclude_from_bind_all:
                            type: boolean
                          ignore_if_unjoinable:
                            type: boolean
                          isFiscal:
                            type: boolean
                          is_negative:
                            type: boolean
                            nullable: true
                          kind:
                            type: string
                            enum:
                              - IS_ON_DAY_OF_WEEK
                              - IS_ON_DAY_OF_QUARTER
                              - IS_IN_MONTH_OF_YEAR
                              - IS_ON_DAY_OF_YEAR
                              - IS_AT_HOUR_OF_DAY
                              - IS_IN_QUARTER_OF_YEAR
                              - IS_IN_WEEK_OF_YEAR
                              - IS_ON_DAY_OF_MONTH
                              - BETWEEN
                              - ON_OR_AFTER
                              - BEFORE
                              - TIME_FOR_INTERVAL_DURATION
                              - TIME_FOR_UNIT_DURATION
                              - QUERY_OFFSET
                          left_side:
                            type: string
                            nullable: true
                          offset_interval_string:
                            type: string
                            nullable: true
                          right_side:
                            type: string
                            nullable: true
                          type:
                            type: string
                            enum:
                              - date
                          ui_type:
                            type: string
                            nullable: true
                            enum:
                              - ANY_TIME
                              - BEFORE
                              - BETWEEN
                              - MONTH_OF_YEAR
                              - PAST
                              - YEAR
                              - DAY
                              - IS_ON_DAY_OF_WEEK
                              - ON_OR_AFTER
                              - IS_IN_THE_MONTH
                              - IS_IN_THE_QUARTER
                              - IS_IN_THE_FISCAL_QUARTER
                              - IS_IN_THE_FISCAL_YEAR
                              - TIME_FOR_INTERVAL_DURATION
                              - TIME_FOR_UNIT_DURATION
                              - CUSTOM
                              - null
                        required:
                          - kind
                          - type
                      - type: object
                        properties:
                          cancel_query_filter:
                            type: boolean
                          exclude_from_bind_all:
                            type: boolean
                          ignore_if_unjoinable:
                            type: boolean
                          is_negative:
                            type: boolean
                            nullable: true
                          type:
                            type: string
                            enum:
                              - 'null'
                        required:
                          - type
                      - type: object
                        properties:
                          cancel_query_filter:
                            type: boolean
                          exclude_from_bind_all:
                            type: boolean
                          ignore_if_unjoinable:
                            type: boolean
                          treat_nulls_as_false:
                            type: boolean
                          is_negative:
                            type: boolean
                            nullable: true
                          type:
                            type: string
                            enum:
                              - boolean
                        required:
                          - type
                      - type: object
                        properties:
                          cancel_query_filter:
                            type: boolean
                          exclude_from_bind_all:
                            type: boolean
                          ignore_if_unjoinable:
                            type: boolean
                          disregard_limit:
                            type: boolean
                          field_name:
                            type: string
                          is_negative:
                            type: boolean
                            nullable: true
                          query_id:
                            type: string
                          type:
                            type: string
                            enum:
                              - query
                          view_query:
                            type: object
                            properties:
                              fields:
                                type: array
                                items:
                                  type: string
                              filters:
                                type: object
                                additionalProperties: {}
                                default: {}
                              limit:
                                type: number
                              sorts:
                                type: array
                                items: {}
                                default: []
                              table:
                                type: string
                            required:
                              - fields
                            additionalProperties: {}
                        required:
                          - type
                      - type: object
                        properties:
                          cancel_query_filter:
                            type: boolean
                          exclude_from_bind_all:
                            type: boolean
                          ignore_if_unjoinable:
                            type: boolean
                          type:
                            type: string
                            enum:
                              - user_attribute
                          user_attribute_name:
                            type: string
                        required:
                          - type
                          - user_attribute_name
                      - type: object
                        properties:
                          cancel_query_filter:
                            type: boolean
                          exclude_from_bind_all:
                            type: boolean
                          ignore_if_unjoinable:
                            type: boolean
                          conjunction:
                            type: string
                            enum:
                              - OR
                              - AND
                          filters:
                            type: array
                            items:
                              $ref: '#/components/schemas/CompositeFilter'
                          is_negative:
                            type: boolean
                            nullable: true
                          type:
                            type: string
                            enum:
                              - composite
                        required:
                          - conjunction
                          - filters
                          - type
                      - type: object
                        properties: {}
                        additionalProperties: {}
                        description: 'A filter written as in model YAML, naming one filter type: `{ "greater_than": 100 }`, `{ "time_for_duration": ["30 days ago", "30 days"] }`, `{ "contains": "x", "case_insensitive": true }`.'
                    description: A value, or an array of values, filters the field to equal it. An object is a full Omni query filter, or names any other filter the way model YAML does.
                  description: Filters to apply for this run, keyed by field name (e.g. `orders.status`). Each replaces the saved filter on the same field and the saved query is not modified. A filter that cannot be applied fails the request.
                  example:
                    chargers.id: CH-1042
                formatResults:
                  type: boolean
                  default: true
                  description: Whether to format result values (e.g., apply number formatting).
                resultType:
                  type: string
                  enum:
                    - csv
                    - xlsx
                    - json
                  description: |
                    Format of the result document. For `csv` and `xlsx` results formats, the document's **Download** setting must be enabled.
                  example: json
              additionalProperties: false
            examples:
              scopeToOneValue:
                summary: Scope a run to one value
                value:
                  filters:
                    chargers.id: CH-1042
              relativeDateRange:
                summary: Scope a run to a relative date range
                value:
                  filters:
                    sessions.started_at:
                      time_for_duration:
                        - 30 days ago
                        - 30 days
      responses:
        '200':
          description: The saved query ran; the body is its result document.
          content:
            application/json:
              schema:  # DocumentsRunQueryJsonResponse
                type: array
                items:
                  type: object
                  additionalProperties:
                    anyOf:
                      - type: string
                      - type: number
                      - type: boolean
                      - type: 'null'
                description: |
                  Result rows keyed by field name. Returned when `resultType` is `json`.
              examples:
                jsonResults:
                  summary: JSON results
                  value:
                    - chargers.id: CH-1042
                      sessions.kwh: 42.5
            application/vnd.ms-excel:
              schema:
                description: |
                  XLSX document. Returned only when `resultType` is `xlsx`.
                format: binary
                type: string
            text/csv:
              schema:
                description: |
                  CSV document. Returned only when `resultType` is `csv`.
                type: string
        '400':
          description: |
            Invalid request. Common causes:

            - An unknown key in the request body
            - The saved query has no runnable definition
            - A filter could not be applied
            - The query failed to run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              examples:
                filterNotApplied:
                  value:
                    detail: 'Filter on "chargers.id" was not applied: an empty array matches nothing; Filter on "sessions.kwh" was not applied: a filter object names exactly one filter type — one key, plus any modifiers'
                    status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Forbidden. Common causes:

            - No view access to the document
            - No access to the workbook, for a saved query the dashboard doesn't show
            - No permission to query the saved query's model
            - The result would be a download (`csv` or `xlsx`), and downloads aren't permitted (Enable the document's **Download** setting)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found. Common causes:

            - The document doesn't exist, or has been archived
            - The identifier names an unsaved analysis or a draft, which have no saved queries to run
            - The document has no saved query with this `queryId`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '408':
          description: |
            The query timed out. Poll the [Wait for query results endpoint](/api/queries/wait-for-query-results) with the `remaining_job_ids` from the response body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QueryTimeoutResponse'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}:
    get:
      tags:
        - Documents
      summary: Get dashboard document
      deprecated: true
      description: |
        <Warning>
          This endpoint is deprecated. Use the [Documents v2 API](/api/documents-v2/get-document-state) instead.
        </Warning>

        Retrieve dashboard document configuration in a format compatible with PUT for round-trip editing. This endpoint returns all fields needed to modify and update a dashboard document.

        **Note:** This endpoint only supports dashboard documents. Workbook-only documents are not supported and will return a `400 Bad Request` error.
      security:
        - bearerAuth: []
      operationId: getDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            minLength: 2
            maxLength: 48
            pattern: ^[a-zA-Z0-9\-_]{2,48}$
            not:
              enum: [clone, dashboard, dashboards, delete, download, edit, move, new, rename, run, schedule, schedules, settings, share, themes]
          description: |
            The document identifier. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.

            Must be 2–48 characters, containing only letters (a-z, A-Z), digits (0-9), hyphens (-), and underscores (_). Cannot be a reserved value.
      responses:
        '200':
          description: Document retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    description: The name of the document
                  modelId:
                    type: string
                    format: uuid
                    description: The ID of the model the document is built on
                  facetFilters:
                    type: boolean
                    description: Whether facet filters are enabled for the document
                  description:
                    type: string
                    description: The description of the document
                  refreshInterval:
                    type: integer
                    description: Auto-refresh interval in seconds
                  filterConfig:
                    type: object
                    description: Filter configuration for the document
                  filterOrder:
                    type: array
                    items:
                      type: string
                    description: Array of filter field names defining the order of filters
                  queryPresentations:
                    type: array
                    description: Array of query presentation objects representing tabs in the document's workbook
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                          description: Name of the query tab
                        query:
                          type: object
                          description: Full OmniQuery object with all query fields
                          properties:
                            fields:
                              type: array
                              items:
                                type: string
                              description: Array of field names to include in the query
                            table:
                              type: string
                              description: The table to query from
                            limit:
                              type: integer
                              description: Maximum number of rows to return
                            filters:
                              type: object
                              description: Filter conditions for the query
                            sorts:
                              type: array
                              items:
                                type: object
                              description: Sort order for the query results
                            modelId:
                              type: string
                              format: uuid
                              description: The model ID for the query
                        chartType:
                          type: string
                          description: Chart type (e.g., "bar", "line", "area", "table")
                        visualization:
                          type: object
                          description: Visualization configuration
                          properties:
                            visType:
                              type: string
                              description: The visualization type (e.g., "basic")
                            config:
                              type: object
                              description: Visualization-specific configuration
                            fields:
                              type: array
                              items:
                                type: string
                              description: Fields used in the visualization
                        prefersChart:
                          type: boolean
                          description: Whether the query prefers chart view over table view
                        id:
                          type: string
                          format: uuid
                          description: Query presentation ID
                        queryIdentifierMapKey:
                          type: string
                          description: Query identifier map key
                        description:
                          type: string
                          description: Description of the query (if set)
                        subTitle:
                          type: string
                          description: Subtitle for the query (if set)
                        topicName:
                          type: string
                          description: Topic name associated with the query (if set)
                        aiConfig:
                          type: object
                          description: AI configuration for the query
                        resultConfig:
                          type: object
                          description: Result table configuration
              example:
                name: "Sales Dashboard"
                modelId: "abc123de-f456-7890-abcd-ef1234567890"
                facetFilters: true
                refreshInterval: 300
                filterConfig:
                  date_range:
                    type: "date"
                    defaultValue: "last_7_days"
                filterOrder:
                  - "date_range"
                  - "region"
                queryPresentations:
                  - name: "Revenue by Region"
                    query:
                      fields:
                        - "order_items.total_revenue"
                        - "orders.region"
                      table: "order_items"
                      limit: 1000
                      filters: {}
                      sorts: []
                      modelId: "abc123de-f456-7890-abcd-ef1234567890"
                    chartType: "bar"
                    prefersChart: true
                    visualization:
                      visType: "basic"
                      config:
                        xAxis: "orders.region"
                        yAxis: "order_items.total_revenue"
                      fields:
                        - "order_items.total_revenue"
                        - "orders.region"
                    id: "cd54494d-318f-4105-af1c-10af59e62b2e"
                    queryIdentifierMapKey: "1"
                    aiConfig: {}
                    resultConfig: {}
        '400':
          description: |
            Bad Request

            Possible error messages:
            - `Analysis documents are not supported`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Analysis documents are not supported"
                status: 400
        '403':
          description: |
            Forbidden. User does not have permission to view the document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User does not have permission to view this document"
                status: 403
        '404':
          description: |
            Not Found

            Possible error messages:
            - `Document with identifier "<documentId>" not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Document with identifier \"054518c8\" not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Documents
      summary: Delete document
      description: Delete a document (move to Trash)
      security:
        - bearerAuth: []
      operationId: deleteDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            minLength: 2
            maxLength: 48
            pattern: ^[a-zA-Z0-9\-_]{2,48}$
            not:
              enum: [clone, dashboard, dashboards, delete, download, edit, move, new, rename, run, schedule, schedules, settings, share, themes]
          description: |
            The ID of the document to delete. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.

            Must be 2–48 characters, containing only letters (a-z, A-Z), digits (0-9), hyphens (-), and underscores (_). Cannot be a reserved value.
      responses:
        '200':
          description: Document deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '404':
          description: |
            Not Found

            Possible error messages:
            - `Document with identifier "<documentId>" not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    patch:
      tags:
        - Documents
      summary: Update document
      deprecated: true
      description: |
        <Warning>
          This endpoint has been removed as of July 31, 2026 and always returns a `410` error.

          Use the [Documents v2 API](/api/documents-v2/patch-draft) instead.
        </Warning>

        Update the name, description, and/or identifier of a document. **Note**: At least one of `name`, `description`, or `identifier` must be provided or the API will return a `Bad Request` error.

        Renaming a document follows the app's draft/publish workflow:

        | Document Type | Behavior |
        |---------------|----------|
        | **Draft** | Renamed directly |
        | **Published (no existing draft)** | Creates draft > Renames draft > Publishes draft |
        | **Published (existing draft, `clearExistingDraft: true`)** | Discards existing draft > Creates new draft > Renames > Publishes |
        | **Published (existing draft, `clearExistingDraft` not set)** | Returns `409 Conflict` |
      security:
        - bearerAuth: []
      operationId: renameDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: Document identifier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 255
                  description: New name for the document.
                description:
                  type: string
                  maxLength: 1024
                  description: Updated description of the document. Set as `null` to clear the description.
                clearExistingDraft:
                  type: boolean
                  description: |
                    If `true`, discards any existing draft before renaming.

                    **Note**: This parameter is required when a draft exists for the document.
                identifier:
                  type: string
                  description: |
                    New identifier for the document. Renaming the identifier works with published documents and maintains old URLs via automatic redirects.

                    When you change a document's identifier:
                    - Both the workbook and document identifiers are updated in one transaction
                    - A `DocumentIdentifierHistory` entry is created so requests to the old identifier automatically redirect to the new one
                    - Dashboard and document history entries are updated

                    **Format requirements:**
                    - Must match the pattern: lowercase letters, numbers, hyphens, and underscores only
                    - Cannot start or end with a hyphen or underscore
                    - Must be unique across all documents in your organization

                    **Common use case:** Migrating content between environments (e.g., staging → production) while maintaining specific identifiers.
                  example: "sales-dashboard-archived"
            examples:
              renameIdentifier:
                summary: Rename document identifier only
                value:
                  identifier: "sales-dashboard-archived"
              updateNameAndIdentifier:
                summary: Update both name and identifier
                value:
                  name: "Q2 2026 Sales Report"
                  identifier: "q2-sales-report"
              renameName:
                summary: Rename document name only
                value:
                  name: "Updated Dashboard Name"
      responses:
        '200':
          description: Document renamed successfully
          headers:
            Deprecation:
              description: Indicates that this endpoint is deprecated
              schema:
                type: string
                example: "true"
            Sunset:
              description: The date this endpoint will be removed (RFC 9110)
              schema:
                type: string
                format: date
                example: "2026-07-31"
            Link:
              description: Link to the replacement API (RFC 8288)
              schema:
                type: string
                example: "</api/documents-v2/create-draft-and-patch-document>; rel=\"successor-version\""
          content:
            application/json:
              schema:
                type: object
                properties:
                  identifier:
                    type: string
                    description: The document identifier
                  name:
                    type: string
                    description: The new document name
              examples:
                renameIdentifier:
                  summary: Rename document identifier
                  value:
                    identifier: "sales-dashboard-archived"
                    name: "Sales Dashboard"
                updateNameAndIdentifier:
                  summary: Update both name and identifier
                  value:
                    identifier: "q2-sales-report"
                    name: "Q2 2026 Sales Report"
        '400':
          description: |
            Bad Request. Possible causes:

            - Request body must have `name`, `description`, or `identifier`
            - Name exceeds 255 characters
            - Description exceeds 1024 characters
            - Identifier format is invalid (must be lowercase letters, numbers, hyphens, and underscores; cannot start/end with hyphen or underscore)
            - Identifier is already in use by another document
            - Draft already exists - set clearExistingDraft to true to discard it and proceed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: User has insufficient permissions to update document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          description: |
            Conflict. Published document has an existing draft and `clearExistingDraft` is not set to `true`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '410':
          $ref: '#/components/schemas/ApiError410'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - Documents
      summary: Update dashboard document
      deprecated: true
      description: |
        <Warning>
          This endpoint has been removed as of July 31, 2026 and always returns a `410` error.

          Use the [Documents v2 API](/api/documents-v2/patch-draft) instead.
        </Warning>

        Update a document with the specified identifier. This endpoint performs a full resource replacement — all required fields must be provided and existing query presentations are replaced entirely.
      x-mint:
        content: |
          The update operation follows the Omni's [draft/publish workflow](/content/develop):

          | Document Type | Behavior |
          |---------------|----------|
          | **Draft** | Updated directly |
          | **Published (no existing draft)** | Creates draft > Updates draft > Publishes draft |
          | **Published (existing draft, `clearExistingDraft: true`)** | Discards existing draft > Creates new draft > Updates > Publishes |
          | **Published (existing draft, `clearExistingDraft` not set)** | Returns `409 Conflict` |

          This endpoint follows Omni's draft/publish workflow. Updates to published documents create a draft, apply changes, then publish. Use the `clearExistingDraft` parameter to discard any existing draft before updating.
      security:
        - bearerAuth: []
      operationId: updateDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: Document identifier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - modelId
                - name
                - facetFilters
                - refreshInterval
                - filterConfig
                - filterOrder
                - queryPresentations
              properties:
                modelId:
                  type: string
                  description: Model ID for query transformation
                name:
                  type: string
                  minLength: 1
                  maxLength: 254
                  description: Document name
                description:
                  type: string
                  maxLength: 1024
                  description: Optional description of the document. Set as `null` to clear the description.
                  example: "Total monthly sales. Used in sales review meetings."
                facetFilters:
                  type: boolean
                  description: When `true`, enable facet filters on the dashboard
                refreshInterval:
                  oneOf:
                    - type: integer
                      minimum: 60
                    - type: "null"
                  description: Auto-refresh interval in seconds. Minimum value must be `60`. Use `null` to disable.
                filterConfig:
                  type: object
                  description: |
                    Dashboard filter configuration. Pass `{}` for no filters.

                    Each key is a field name and the value is a filter definition object.
                  additionalProperties:
                    type: object
                    properties:
                      type:
                        type: string
                        description: Field type (e.g., "string", "number")
                      kind:
                        type: string
                        description: Filter kind (e.g., "EQUALS", "GREATER_THAN")
                      values:
                        type: array
                        items: {}
                        description: Filter values
                      is_negative:
                        type: boolean
                        description: When `true`, negate the filter condition
                filterOrder:
                  type: array
                  items:
                    type: string
                  description: Order of filters in the filter bar. Pass `[]` if no filters.
                queryPresentations:
                  type: array
                  minItems: 1
                  description: Array of query presentations (at least one required)
                  items:
                    type: object
                    required:
                      - name
                      - query
                    properties:
                      name:
                        type: string
                        description: Name of the query tab
                      query:
                        type: object
                        required:
                          - fields
                          - table
                        properties:
                          fields:
                            type: array
                            items:
                              type: string
                            description: Array of field names to include in the query
                          table:
                            type: string
                            description: Table name for the query
                        description: Query definition
                      description:
                        type: string
                        description: Query description
                      subTitle:
                        type: string
                        description: Subtitle for the query
                      prefersChart:
                        type: boolean
                        description: When `true`, show chart by default
                      topicName:
                        type: string
                        description: Topic name for the query
                      chartType:
                        oneOf:
                          - type: string
                          - type: "null"
                        description: Chart type (line, bar, etc.)
                      visConfig:
                        type: object
                        description: Full visualization configuration
                      resultConfig:
                        type: object
                        description: Result display configuration
                      aiConfig:
                        type: object
                        description: AI configuration
                documentMetadata:
                  type: object
                  description: Document metadata for presentation settings
                  properties:
                    presentation:
                      type: object
                      properties:
                        filters:
                          type: object
                          properties:
                            collapsible:
                              type: boolean
                              description: Whether filters are collapsible
                            defaultExpanded:
                              type: boolean
                              description: Whether filters are expanded by default
                clearExistingDraft:
                  type: boolean
                  description: |
                    If `true`, discards any existing draft before updating.

                    **Note**: This parameter is required when a draft exists for the document.
            example:
              modelId: "abc123"
              name: "Monthly Sales Dashboard"
              description: "Total monthly sales. Used in sales review meetings."
              facetFilters: true
              refreshInterval: null
              filterConfig:
                order_items.category:
                  type: "string"
                  kind: "EQUALS"
                  values:
                    - "Electronics"
                  is_negative: false
              filterOrder:
                - "order_items.category"
              queryPresentations:
                - name: "Sales Trend"
                  description: "Monthly sales over time"
                  prefersChart: true
                  topicName: "order_items"
                  query:
                    fields:
                      - "order_items.created_at[month]"
                      - "order_items.sale_price_sum"
                    table: "order_items"
                  visConfig:
                    visType: "basic"
                    spec:
                      mark:
                        type: "line"
              documentMetadata:
                presentation:
                  filters:
                    collapsible: true
                    defaultExpanded: false
      responses:
        '200':
          description: Document updated successfully
          headers:
            Deprecation:
              description: Indicates that this endpoint is deprecated
              schema:
                type: string
                example: "true"
            Sunset:
              description: The date this endpoint will be removed (RFC 9110)
              schema:
                type: string
                format: date
                example: "2026-07-31"
            Link:
              description: Link to the replacement API (RFC 8288)
              schema:
                type: string
                example: "</api/documents-v2/create-document>; rel=\"successor-version\""
          content:
            application/json:
              schema:
                type: object
                properties:
                  identifier:
                    type: string
                    description: The document identifier
                  name:
                    type: string
                    description: The updated document name
                  description:
                    type: string
                    description: The updated document description
              example:
                identifier: "my-dashboard"
                name: "Q4 Sales Dashboard"
                description: "Total monthly sales. Used in sales review meetings."
        '400':
          description: |
            Bad Request. Possible causes:

            - Missing required fields (`modelId`, `name`, `facetFilters`, `refreshInterval`, `filterConfig`, `filterOrder`, `queryPresentations`)
            - Invalid field values (`name` too long, `refreshInterval` less than `60`)
            - Empty `queryPresentations` array (at least one required)
            - Document has no dashboard (workbook-only document)
            - Analysis documents (workbooks or documents without a dashboard) are not supported
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: User has insufficient permissions to update document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: |
            Conflict. Draft already exists - set clearExistingDraft to true to discard it and proceed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '410':
          $ref: '#/components/schemas/ApiError410'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/access-list:
    get:
      tags:
        - Document permissions
      summary: List all users and groups with document access
      description: |
        List all users and groups with access to a document. 
        
        The paginated response includes a list of `principal` objects, where each entry represents a distinct access grant with its own role and settings. A `principal` may appear twice in the response if they have both `direct` access and `folder`-based access to the same document. 
      security:
        - bearerAuth: []
      operationId: listDocumentAccessList
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The document identifier. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
        - name: pageSize
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Number of results per page (1-100).
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Pagination cursor from a previous response's `pageInfo.nextCursor`.
        - name: sortField
          in: query
          required: false
          schema:
            type: string
            enum: [name, email, role]
            default: name
          description: Field to sort results by.
        - name: sortDirection
          in: query
          required: false
          schema:
            type: string
            enum: [asc, desc]
            default: asc
          description: Sort order.
        - name: accessSource
          in: query
          required: false
          schema:
            type: string
            enum: [direct, folder]
          description: |
            Filter by how access was granted:
            - `direct` — Only principals with explicit document permissions
            - `folder` — Only principals with inherited folder permissions
        - name: type
          in: query
          required: false
          schema:
            type: string
            enum: [user, userGroup]
          description: |
            Filter by principal type:
            - `user` — Only individual users
            - `userGroup` — Only user groups
      responses:
        '200':
          description: Successfully retrieved access list
          content:
            application/json:
              schema:
                type: object
                properties:
                  principals:
                    type: array
                    items:
                      $ref: '#/components/schemas/DocumentAccessPrincipal'
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
              example:
                principals:
                  - id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    userId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    name: "Jane Smith"
                    email: "jane@example.com"
                    type: "user"
                    role: "EDITOR"
                    accessBoost: false
                    accessSource: "direct"
                    isOwner: false
                  - id: "b2c3d4e5-f6a7-8901-bcde-f23456789012"
                    userId: "b2c3d4e5-f6a7-8901-bcde-f23456789012"
                    name: "John Doe"
                    email: "john@example.com"
                    type: "user"
                    role: "VIEWER"
                    accessBoost: false
                    accessSource: "folder"
                    isOwner: false
                    folderInfo:
                      id: "c3d4e5f6-a7b8-9012-cdef-345678901234"
                      name: "Marketing Reports"
                      path: "/Shared/Marketing Reports"
                  - id: "d4e5f6a7-b8c9-0123-def0-456789012345"
                    userGroupId: "d4e5f6a7-b8c9-0123-def0-456789012345"
                    name: "Data Analysts"
                    type: "userGroup"
                    role: "VIEWER"
                    accessBoost: false
                    accessSource: "direct"
                pageInfo:
                  hasNextPage: true
                  nextCursor: "eyJuYW1lIjoiSm9obiIsImlkIjoiMTIzIn0="
                  pageSize: 20
                  totalRecords: 47
        '400':
          description: |
            Bad Request. Possible causes:

            - Invalid `pageSize` value (must be 1-100)
            - Invalid `sortField` value
            - Invalid `sortDirection` value
            - Invalid `accessSource` value
            - Invalid `type` value
            - Invalid `cursor` value
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                invalidPageSize:
                  summary: Invalid pageSize value
                  value:
                    detail: "pageSize: Must be between 1 and 100"
                    status: 400
                invalidSortField:
                  summary: Invalid sortField value
                  value:
                    detail: "sortField: Must be one of: name, email, role"
                    status: 400
        '403':
          description: |
            Forbidden. The user sending the API request must have **Manager** permissions for the document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User does not have permission to manage document permissions"
                status: 403
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Document with identifier \"<documentId>\" not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/favorite:
    put:
      tags:
        - Document favorites
      summary: Favorite document
      description: |
        Add a document to a user's favorites. Only published documents can be favorited.

        **Note**: Successful requests will return `204` regardless of whether the document is newly favorited or already in the user's favorites.
      security:
        - bearerAuth: []
      operationId: favoriteDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The document identifier
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Membership ID of the user to favorite the document on behalf of. 

            Personal Access Tokens (PATs) cannot use this parameter to act on behalf of other users.
      responses:
        '204':
          description: |
            Document favorited successfully. No response body.

            This response is returned whether the document was newly favorited or already in favorites.
        '403':
          description: |
            Forbidden. Possible causes:

            - Personal Access Token attempted to act on behalf of another user. PATs cannot use the `userId` parameter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible causes:

            - Document does not exist
            - Document is not published
            - Specified `userId` not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Document favorites
      summary: Unfavorite document
      description: |
        Remove a document from a user's favorites. Only published documents can be unfavorited.

        **Note**: Successful requests will return `204` regardless of whether the document was previously favorited or not.
      security:
        - bearerAuth: []
      operationId: unfavoriteDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The document identifier
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Membership ID of the user to unfavorite the document on behalf of.

            Personal Access Tokens (PATs) cannot use this parameter to act on behalf of other users.
      responses:
        '204':
          description: |
            Document unfavorited successfully. No response body.

            This response is returned whether the document was previously favorited or not.
        '400':
          description: Invalid HTTP method
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden. Possible causes:

            - Personal Access Token attempted to act on behalf of another user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible causes:

            - Document does not exist
            - Document is not published
            - Specified `userId` not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/favorites:
    get:
      tags:
        - Document favorites
      summary: List document favoriters
      description: |
        <Note>
          This endpoint requires **Manager** or **Owner** permissions on the requested document.
        </Note>
        
        Lists users who have favorited a published document, paginated and sorted by `favoritedAt`.

        Use this endpoint when you need to know "who favorited document X" — for example, a content-migration script that preserves favorites when replacing a document needs to call this once per document, rather than iterating every user in the organization.
      security:
        - orgApiKey: []
      operationId: listDocumentFavoriters
      parameters:
        - name: identifier
          in: path
          required: true
          schema:
            type: string
          description: Document identifier (either document ID or slug).
        - name: cursor
          in: query
          required: false
          schema:
            type: string
            nullable: true
          description: Page cursor from a previous response's `nextCursor`. Omit for the first page.
        - name: pageSize
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Number of items per page (min 1, max 100).
        - name: sortDirection
          in: query
          required: false
          schema:
            type: string
            enum: [asc, desc]
            default: asc
          description: Sort direction by `favoritedAt`. `asc` returns oldest favorites first; `desc` returns newest first.
      responses:
        '200':
          description: Successfully retrieved list of users who favorited the document
          content:
            application/json:
              schema:
                type: object
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    items:
                      type: object
                      properties:
                        userId:
                          type: string
                          description: Membership ID of the favoriting user.
                        name:
                          type: string
                          description: User's display name.
                        email:
                          type: string
                          description: User's email address.
                        favoritedAt:
                          type: string
                          format: date-time
                          description: ISO 8601 timestamp recording when the user favorited the document.
                      required:
                        - userId
                        - name
                        - email
                        - favoritedAt
                required:
                  - pageInfo
                  - records
              examples:
                basicResponse:
                  summary: Basic request with 2 favoriters
                  value:
                    pageInfo:
                      hasNextPage: false
                      nextCursor: null
                      pageSize: 20
                      totalRecords: 2
                    records:
                      - userId: f1c2a3e4-1111-1111-1111-111111111111
                        name: Blob Ross
                        email: blob.ross@eblobsrus.com
                        favoritedAt: "2026-04-12T10:14:02.000Z"
                      - userId: f1c2a3e4-2222-2222-2222-222222222222
                        name: Blob the Builder
                        email: blob.the.builder@blobsrus.com
                        favoritedAt: "2026-05-01T17:33:21.000Z"
                emptyResponse:
                  summary: Document with no favoriters
                  value:
                    pageInfo:
                      hasNextPage: false
                      nextCursor: null
                      pageSize: 20
                      totalRecords: 0
                    records: []
        '400':
          description: |
            Bad Request. Invalid query parameters.

            Possible causes:
            - Unparseable cursor
            - Out-of-range `pageSize` (must be 1-100)
            - Invalid `sortDirection` (must be `asc` or `desc`)
            - Unknown query parameter
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                  status:
                    type: integer
              examples:
                invalidCursor:
                  summary: Invalid cursor
                  value:
                    detail: "Invalid cursor: not-a-number"
                    status: 400
                invalidPageSize:
                  summary: Page size out of range
                  value:
                    detail: "pageSize must be between 1 and 100"
                    status: 400
                unknownParam:
                  summary: Unknown query parameter
                  value:
                    detail: "Unrecognized key: random_bogus_param"
                    status: 400
        '401':
          description: Missing authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden. Possible causes:
            - Caller lacks `MANAGER` permission on the document
            - Used a user-scoped (Personal Access Token) API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                  status:
                    type: integer
              examples:
                insufficientPermission:
                  summary: Insufficient permission on document
                  value:
                    detail: "You do not have permission to manage document access."
                    status: 403
                userScopedKey:
                  summary: User-scoped API key not allowed
                  value:
                    detail: "User-scoped API keys are not allowed to list document favoriters"
                    status: 403
        '404':
          description: Document not found (or unpublished)
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                  status:
                    type: integer
              examples:
                notFound:
                  summary: Document not found
                  value:
                    detail: 'Document with identifier "does-not-exist" not found'
                    status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/labels:
    patch:
      tags:
        - Document labels
      summary: Bulk update document labels
      description: Add and/or remove multiple labels from a document in a single atomic operation.
      x-mint:
        content: |
          When using this endpoint, keep in mind that:

          - **All changes succeed or fail together.** No partial updates occur.
          - **Label matching is case-insensitive**.
          - **Requests must have at least one operation.** Either `add` or `remove` must contain at least one label.
          - **Labels must already exist to be added to or removed from a document.** Create labels via the [Create label API](/api/labels/create-label).
          - **Labels cannot be included in both `add` and `remove` in the same request.**
          - **Organization Admin permissions are required to**:
             - Add or remove **Verified** labels
             - Add or remove **Homepage** labels
      security:
        - bearerAuth: []
      operationId: bulkUpdateDocumentLabels
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The document identifier (UUID)
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Optional user ID that attributes the action to the specified user.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                add:
                  type: array
                  items:
                    type: string
                    minLength: 2
                    maxLength: 25
                  default: []
                  description: Label names to add to the document
                remove:
                  type: array
                  items:
                    type: string
                    minLength: 2
                    maxLength: 25
                  default: []
                  description: Label names to remove from the document
            examples:
              addLabels:
                summary: Add labels
                value:
                  add:
                    - production
                    - reviewed
              removeLabels:
                summary: Remove labels
                value:
                  remove:
                    - draft
                    - needs-review
              addAndRemove:
                summary: Add and remove labels
                value:
                  add:
                    - approved
                  remove:
                    - pending-review
      responses:
        '200':
          description: Labels updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  labels:
                    type: array
                    items:
                      type: string
                    description: The updated list of labels on the document
              example:
                labels:
                  - label-one
                  - label-two
                  - new-label
        '400':
          description: |
            Bad Request. Possible causes:

            - Empty request - Neither `add` nor `remove` contains any labels
            - Label appears in both `add` and `remove` arrays
            - Invalid label name (less than 2 or more than 25 characters)
            - Unrecognized field in the request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                emptyRequest:
                  summary: Empty request
                  value:
                    detail: "Bad Request: At least one label must be specified in add or remove"
                    status: 400
                    title: Bad Request
                conflictingLabels:
                  summary: Label in both arrays
                  value:
                    detail: "Bad Request: Labels cannot appear in both add and remove arrays"
                    status: 400
                    title: Bad Request
                unrecognizedField:
                  summary: Unrecognized field
                  value:
                    detail: "Bad Request: Unrecognized field in request body"
                    status: 400
                    title: Bad Request
        '403':
          description: |
            Forbidden. Possible causes:

            - User does not have `canLabel` permission on the document
            - User lacks Organization Admin permissions to modify Verified labels
            - User lacks Organization Admin permissions to modify Homepage labels
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                permissionDenied:
                  summary: Permission denied
                  value:
                    detail: You do not have permission to modify labels on this document.
                    status: 403
                    title: Forbidden
                verifiedLabelDenied:
                  summary: Verified label permission denied
                  value:
                    detail: You do not have permission to modify Verified labels on this document
                    status: 403
                    title: Forbidden
        '404':
          description: |
            Not Found. Possible causes:

            - Document does not exist
            - Label does not exist globally. Create the label first with the [Create label API](/api/labels/create-label).
            - Label in `remove` array does not exist on the document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                documentNotFound:
                  summary: Document not found
                  value:
                    detail: Document with identifier "abc123" not found
                    status: 404
                    title: Not Found
                labelNotFound:
                  summary: Label not found globally
                  value:
                    detail: Label "my-label" not found
                    status: 404
                    title: Not Found
                labelNotOnDocument:
                  summary: Label not on document
                  value:
                    detail: |
                      Labels not found on this document: "label-one", "label-two"
                    status: 404
                    title: Not Found
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/labels/{labelName}:
    put:
      tags:
        - Document labels
      summary: Apply label to document
      description: |
        Apply an existing label to a document. Labels must be created first via the [Create label](/api/labels/create-label) endpoint.

        Documents can have multiple labels. When a new label is applied using this endpoint, the API adds it to the document's existing labels. Labels are not replaced.
      security:
        - bearerAuth: []
      operationId: applyLabelToDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The document identifier
        - name: labelName
          in: path
          required: true
          schema:
            type: string
            minLength: 2
            maxLength: 25
          description: |
            The label name to apply:
            
            - Must be 2-25 characters
            - Labels are **case insensitive**. For example, adding `BlobSales` when `blobsales` exists will be treated as a duplicate. 
            - Special characters must be URL-encoded (e.g., `Q1%202024` for "Q1 2024").

            Additionally, adding **Verified** or **Homepage** labels require Organization Admin permissions.
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Optional user ID that attributes the action to the specified user.
      responses:
        '204':
          description: |
            Label applied successfully. No response body.

            This response is returned whether the label was newly applied or already existed on the document.
        '400':
          description: |
            Bad Request. Possible causes:

            - Label name too short (less than 2 characters)
            - Label name too long (more than 25 characters)
            - Invalid HTTP method
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden. Possible causes:

            - User does not have Manager role on the specified document
            - User lacks Organization Admin permissions for Verified labels
            - User lacks Organization Admin permissions for Homepage labels
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible causes:

            - Document does not exist
            - Label does not exist. Must be created first via the [Create label](/api/labels/create-label).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Document labels
      summary: Remove label from document
      description: Remove a label from a document.
      x-mint: 
        content: |
          <Note>
            This endpoint is not idempotent. If the label does not exist on the document, the API returns a `404` error.
          </Note>
      security:
        - bearerAuth: []
      operationId: removeLabelFromDocument
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The document identifier
        - name: labelName
          in: path
          required: true
          schema:
            type: string
            minLength: 2
            maxLength: 25
          description: |
            The label name to remove:

            - Must be 2-25 characters
            - Labels are **case insensitive**. For example, removing `BlobSales` will remove `blobsales` if it exists on the document.
            - Special characters must be URL-encoded (e.g., `Q1%202024` for "Q1 2024").
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Optional user ID that attributes the action to the specified user.
      responses:
        '204':
          description: Label removed successfully. No response body.
        '403':
          description: |
            Forbidden. User does not have permission to modify labels on this document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible causes:

            - Document does not exist
            - Label does not exist on this document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: Label "production" not found on this document
                status: 404
                title: Not Found
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/documents/{documentId}/permissions:
    post:
      tags:
        - Document permissions
      summary: Grant document permissions
      description: Grant document permissions to users or groups.
      security:
        - bearerAuth: []
      operationId: grantDocumentPermissions
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The document identifier. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - role
              properties:
                role:
                  type: string
                  enum: [NO_ACCESS, VIEWER, EDITOR, MANAGER, OWNER]
                  description: |
                    The content role to assign. Must be one of:
                    - `NO_ACCESS` - No access. Document won't appear in content system or search results.
                    - `VIEWER` - View dashboard
                    - `EDITOR` - Edit dashboard and workbook
                    - `MANAGER` - Edit dashboard and workbook and manage permissions
                    - `OWNER` - Manager permissions and receives access requests. The document must have at least one owner at all times.
                  example: "EDITOR"
                accessBoost:
                  type: boolean
                  default: false
                  description: |
                    If `true`, [AccessBoost](/share#boosting-permissions-with-accessboost) will be enabled for the document. Setting this to `true` with a PAT requires that the organization's **Non-administrators can enable AccessBoost on content** setting is enabled and that the user has permission to enable AccessBoost. Organization API keys are not subject to this check.
                  example: false
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: |
                    The list of user IDs to assign permissions to. Use the [List users](/api/users/list-users) and [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs. **Either `userIds` or `userGroupIds` must be a non-empty list.**
                  example: ["3c90c3cc-0d44-4b50-8888-8dd25736052a"]
                userGroupIds:
                  type: array
                  items:
                    type: string
                  description: |
                    The list of user group IDs to assign permissions to. Use the [List user groups](/api/user-groups/list-user-groups) endpoint to retrieve user group IDs. **Either `userIds` or `userGroupIds` must be a non-empty list.**
                  example: ["mEhXj6ZI"]
              additionalProperties: false
      responses:
        '200':
          description: Permissions granted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request. Possible causes:

            - Missing `role` parameter
            - Missing `userIds` or `userGroupIds` parameter
            - Invalid `role` or `userId` value
            - An unrecognized field was included
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missingIds:
                  summary: Missing userIds or userGroupIds
                  value:
                    detail: "userIds.userGroupIds: userIds or userGroupIds must be provided"
                    status: 400
                invalidUuid:
                  summary: Invalid userId value
                  value:
                    detail: "userIds.0: Invalid uuid"
                    status: 400
        '403':
          description: |
            Forbidden. Possible causes:

            - The user sending the API request must have **Manager** permissions for the document.
            - The request sets `accessBoost` to `true` with a Personal Access Token (PAT), but the organization's **Non-administrators can enable AccessBoost on content** setting is disabled, or the user does not have permission to enable AccessBoost.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User does not have permission to manage document permissions"
                status: 403
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Document with identifier \"<documentId>\" not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
    patch:
      tags:
        - Document permissions
      summary: Update document permissions
      description: Update existing document permissions for users or groups.
      security:
        - bearerAuth: []
      operationId: updateDocumentPermissions
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The document identifier. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - role
              properties:
                role:
                  type: string
                  enum: [NO_ACCESS, VIEWER, EDITOR, MANAGER, OWNER]
                  description: |
                    The content role to assign. Must be one of:
                    - `NO_ACCESS` - No access. Document won't appear in content system or search results.
                    - `VIEWER` - View dashboard
                    - `EDITOR` - Edit dashboard and workbook
                    - `MANAGER` - Edit dashboard and workbook and manage permissions
                    - `OWNER` - Manager permissions and receives access requests. The document must have at least one owner at all times.
                  example: "EDITOR"
                accessBoost:
                  type: boolean
                  default: false
                  description: |
                    If `true`, [AccessBoost](/share#boosting-permissions-with-accessboost) will be enabled for the document. Setting this to `false` is always allowed. Setting this to `true` with a user-scoped API key requires that the organization's **Non-administrators can enable AccessBoost on content** setting is enabled and that the user has permission to enable AccessBoost, or the request returns a 403 error. Organization-scoped API keys are not subject to this check.
                  example: false
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: |
                    The list of user IDs to assign permissions to. Use the [List users](/api/users/list-users) and [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs. **Either `userIds` or `userGroupIds` must be a non-empty list.**
                  example: ["3c90c3cc-0d44-4b50-8888-8dd25736052a"]
                userGroupIds:
                  type: array
                  items:
                    type: string
                  description: |
                    The list of user group IDs to assign permissions to. Use the [List user groups](/api/user-groups/list-user-groups) endpoint to retrieve user group IDs. **Either `userIds` or `userGroupIds` must be a non-empty list.**
                  example: ["mEhXj6ZI"]
              additionalProperties: false
      responses:
        '200':
          description: Permissions updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request. Possible causes:

            - Missing `role` parameter
            - Missing `userIds` or `userGroupIds` parameter
            - Invalid `role` or `userId` value
            - An unrecognized field was included
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missingIds:
                  summary: Missing userIds or userGroupIds
                  value:
                    detail: "userIds.userGroupIds: userIds or userGroupIds must be provided"
                    status: 400
                invalidUuid:
                  summary: Invalid userId value
                  value:
                    detail: "userIds.0: Invalid uuid"
                    status: 400
        '403':
          description: |
            Forbidden. Possible causes:

            - The user sending the API request must have **Manager** permissions for the document.
            - The request sets `accessBoost` to `true` with a user-scoped API key, but the organization's **Non-administrators can enable AccessBoost on content** setting is disabled, or the user does not have permission to enable AccessBoost.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User does not have permission to manage document permissions"
                status: 403
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Document with identifier \"<documentId>\" not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - Document permissions
      summary: Update document permission settings
      description: |
        Update the permission and [interactivity settings](/share#controlling-document-interactivity) for a document. For example, the ability to allow users to schedule or download the document's content.

        **Note**: Only the fields included in the request will be updated.
      security:
        - bearerAuth: []
      operationId: updateDocumentSettings
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The document identifier. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                organizationRole:
                  type: string
                  enum: [NO_ACCESS, VIEWER, EDITOR, MANAGER]
                  description: |
                    The default content role for the organization. Must be one of:
                    - `NO_ACCESS` - No access. Document won't appear in content system or search results.
                    - `VIEWER` - View dashboard
                    - `EDITOR` - Edit dashboard and workbook
                    - `MANAGER` - Edit dashboard and workbook and manage permissions

                    Pass `null` to clear the organization role.
                canAnalyze:
                  type: boolean
                  description: If `true`, users with required permissions can explore from this document by creating new workbooks. In the UI, this is the **Analyze in new workbook** setting in the document's settings.
                canDownload:
                  type: boolean
                  description: If `true`, users with required permissions will be able to [download the document's query results](/analyze-explore/point-click-queries#downloading-results) or [dashboards](/visualize-present/dashboards/download). In the UI, this is the **Download** setting in the document's settings.
                canDrill:
                  type: boolean
                  description: If `true`, users with required permissions will be able to drill into data points in the document's content. In the UI, this is the **Drill** setting in the document's settings.
                canDuplicate:
                  type: boolean
                  description: If `true`, users with required permissions can duplicate the document. In the UI, this is the **Duplicate** setting in the document's Share settings.
                canRequestAccess:
                  type: boolean
                  description: If `true`, users can request access to the document when they don't have permissions to view it. In the UI, this is the **Request access** setting in the document's settings.
                canSaveSpreadsheets:
                  type: boolean
                  description: If `true`, users with required permissions can create spreadsheets from query results. In the UI, this is the **Save as spreadsheet** setting in the document's settings.
                canSchedule:
                  type: boolean
                  description: If `true`, users with required permissions will be able to create [deliveries (schedules and alerts)](/share/deliveries) on the document. In the UI, this is the **Schedule** setting in the document's settings.
                canUpload:
                  type: boolean
                  description: |
                    If `true`, users with required permissions can [upload data](/analyze-explore/data-input-csvs) (ex: CSVs) into the document to create data input tables. In the UI, this is the **Upload data** setting in the document's settings.
                canUseDashboardAi:
                  type: boolean
                  description: If `true`, users with required permissions can use AI features within the dashboard. In the UI, this is the **Dashboard AI** setting in the document's settings.
                canUseTimezoneOverride:
                  type: boolean
                  description: If `true`, users can override the document's timezone setting. In the UI, this is the **Timezone override** setting in the document's settings.
                canViewWorkbook:
                  type: boolean
                  description: If `true`, users with required permissions can view a read-only version of the workbook. In the UI, this is the **Viewers can see workbook** setting in the document's settings.
                requirePullRequestToPublish:
                  type: boolean
                  description: If `true`, publishing changes to this document requires a pull request through the git integration. In the UI, this is the **Require pull request to publish** setting in the document's settings.
              additionalProperties: false
            examples:
              updateMultipleAbilities:
                summary: Update multiple ability settings
                value:
                  canSaveSpreadsheets: false
                  canRequestAccess: false
                  canDownload: true
              updateOrganizationRole:
                summary: Update organization-wide access
                value:
                  organizationRole: VIEWER
      responses:
        '200':
          description: Settings updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request. Possible causes:

            - Invalid parameter value
            - An unrecognized field was included
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "<parameter>: Invalid <parameter>"
                status: 400
        '403':
          description: |
            Forbidden. Possible causes:

            - The user sending the API request must have **Manager** permissions for the document.
            - The organization's **Organization access for personal content** setting is off, the document is in a personal space (My Documents), and `organizationRole` would raise the Organization role above what the document already holds, directly or from its folder.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                managerRequired:
                  summary: Missing Manager permissions
                  value:
                    detail: "User does not have permission to manage document permissions"
                    status: 403
                personalContentBlocked:
                  summary: Organization access for personal content is off
                  value:
                    detail: "Your organization does not allow sharing personal content with the whole organization. Move it to a shared space first."
                    status: 403
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Document with identifier \"<documentId>\" not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - Document permissions
      summary: Get document permissions
      description: |
        Retrieve the document-level ability values (the **Abilities** toggles in the document's settings), plus the resolved permits for a specific user when `userId` is provided.
      security:
        - bearerAuth: []
      operationId: getDocumentPermissions
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The document identifier. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
        - name: userId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: |
            User membership ID to check permissions for. When omitted, only the document-level abilities are returned. Use the [List users](/api/users/list-users) and [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs.
      responses:
        '200':
          description: Document abilities, plus user permits when `userId` is provided
          content:
            application/json:
              schema:
                type: object
                required:
                  - abilities
                properties:
                  abilities:
                    $ref: '#/components/schemas/DocumentAbilities'
                  permits:
                    type: array
                    description: User permits for the document. Present only when `userId` is provided.
                    items:
                      type: object
                      properties:
                        description:
                          type: string
                          description: Description of the permission type. For example, `Organization`
                        id:
                          type: string
                          description: ID of the user
                        name:
                          type: string
                          description: Name of the user
                        type:
                          type: string
                          description: The type of the permission holder (e.g., "user")
                        direct:
                          type: object
                          description: Direct permissions assigned to the user
                          properties:
                            accessBoost:
                              type: boolean
                              description: If `true`, AccessBoost is enabled for the user
                            isOwner:
                              type: boolean
                              description: If `true`, the user is the owner of the document
                            role:
                              type: string
                              description: The content role assigned to the user
                        folder:
                          type: object
                          description: Permissions inherited from a folder
                          properties:
                            accessBoost:
                              type: boolean
                              description: If `true`, AccessBoost is enabled via folder permissions
                            isOwner:
                              type: boolean
                              description: If `true`, the user is the owner via folder permissions
                            role:
                              type: string
                              description: The content role inherited from folder permissions
              examples:
                withAbilitiesOnly:
                  summary: Abilities only (no userId provided)
                  value:
                    abilities:
                      canAnalyze: true
                      canDownload: false
                      canDrill: false
                      canDuplicate: true
                      canRequestAccess: true
                      canSaveSpreadsheets: false
                      canSchedule: true
                      canUpload: false
                      canUseDashboardAi: false
                      canUseTimezoneOverride: false
                      canViewWorkbook: false
                      requirePullRequestToPublish: false
                withAbilitiesAndPermits:
                  summary: Abilities and permits (userId provided)
                  value:
                    abilities:
                      canAnalyze: true
                      canDownload: false
                      canDrill: false
                      canDuplicate: true
                      canRequestAccess: true
                      canSaveSpreadsheets: false
                      canSchedule: true
                      canUpload: false
                      canUseDashboardAi: false
                      canUseTimezoneOverride: false
                      canViewWorkbook: false
                      requirePullRequestToPublish: false
                    permits:
                      - description: "Organization"
                        direct:
                          accessBoost: false
                          isOwner: false
                          role: "VIEWER"
                        id: "df290ed4-b721-4efe-914b-95d30ce1c5f2"
                        name: "Organization"
                        type: "user"
        '400':
          description: |
            Bad Request. Possible causes:

            - Invalid `userId` value
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "userId: Invalid userId"
                status: 400
        '403':
          description: |
            Forbidden. The user sending the API request must have **Manager** permissions for the document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User does not have permission to manage document permissions"
                status: 403
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Document with identifier \"<documentId>\" not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Document permissions
      summary: Revoke document permissions
      description: Revoke document permissions for users or user groups.
      security:
        - bearerAuth: []
      operationId: revokeDocumentPermissions
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: |
            The document identifier. To retrieve the ID, navigate to **File > Document settings** in the document and then click **Settings**. The **Identifier** field contains the document ID.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: |
                    The list of user IDs to revoke permissions from. Use the [List users](/api/users/list-users) and [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs. **Either `userIds` or `userGroupIds` must be a non-empty list.**
                  example: ["3c90c3cc-0d44-4b50-8888-8dd25736052a"]
                userGroupIds:
                  type: array
                  items:
                    type: string
                  description: |
                    The list of user group IDs to revoke permissions from. Use the [List user groups](/api/user-groups/list-user-groups) endpoint to retrieve user group IDs. **Either `userIds` or `userGroupIds` must be a non-empty list.**
                  example: ["mEhXj6ZI"]
              additionalProperties: false
      responses:
        '200':
          description: Permissions revoked successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request. Possible causes:

            - Missing `userIds` or `userGroupIds` parameter
            - Invalid `userId` value
            - An unrecognized field was included
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missingIds:
                  summary: Missing userIds or userGroupIds
                  value:
                    detail: "userId: userId must be provided"
                    status: 400
                invalidUserId:
                  summary: Invalid userId value
                  value:
                    detail: "userId: Invalid userId"
                    status: 400
        '403':
          description: |
            Forbidden. The user sending the API request must have **Manager** permissions for the document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "User does not have permission to manage document permissions"
                status: 403
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Document with identifier \"<documentId>\" not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/embed/sso/generate-session:
    post:
      operationId: embedSsoGenerateSession
      summary: Generate embedded SSO session
      description: |
        <Note>
          The [Embed](/embed) feature must be enabled in your Omni organization to use this endpoint.
        </Note>

        Generate an embedded SSO session.
      tags:
        - Embed
      requestBody:
        required: true
        content:
          application/json:
            schema:    # EmbedSsoGenerateSessionBody
              type: object
              required:
                - contentPath
                - externalId
                - name
              properties:
                accessBoost:
                  anyOf:
                    - type: string
                      enum:
                        - 'true'
                        - 'false'
                    - type: boolean
                  description: |
                    If `true`, enables [AccessBoost](/embed/setup/url-parameters/accessBoost) for the embedded dashboard. Requires AccessBoost to be enabled for non-administrators in **Settings > Content permissions**.
                  example: true
                branch:
                  type: string
                  not:
                    pattern: '[\r\n]'
                  description: Accepted for compatibility. This endpoint does not apply the branch to the session.
                connectionRoles:
                  type: object
                  additionalProperties:
                    type: string
                  description: |
                    Map of connection IDs to base or custom role names, setting the level of access embed users have to the connection's models. Invalid or disallowed roles are ignored. See the [`connectionRoles` parameter reference](/embed/setup/url-parameters/connectionRoles) for accepted role values and omission behavior.
                  example:
                    65b10d2a-473b-4486-92c8-0ba628c7d1cb: RESTRICTED_QUERIER
                contentPath:
                  type: string
                  minLength: 1
                  not:
                    pattern: '[\r\n]'
                  description: |
                    Path to the Omni content to open, such as `/dashboards/abc123`. See the [`contentPath` parameter reference](/embed/setup/url-parameters/contentPath) for valid starting pages.
                  example: /my
                customTheme:
                  type: object
                  additionalProperties:
                    type: string
                  description: |
                    Custom dashboard theme properties. Values must be a valid [theme property](/embed/customization/themes). See the [`customTheme` parameter reference](/embed/setup/url-parameters/customTheme) for more information.
                customThemeId:
                  type: string
                  not:
                    pattern: '[\r\n]'
                  description: |
                    The ID of a saved dashboard theme. See the [`customThemeId` parameter reference](/embed/setup/url-parameters/customThemeId) for more information.
                email:
                  type: string
                  not:
                    pattern: '[\r\n]'
                  description: |
                    Optional embed user email address. An empty string is treated as omitted; non-empty values must be email-shaped. See the [`email` parameter reference](/embed/setup/url-parameters/email) for its effect on existing email-only users.
                entity:
                  type: string
                  maxLength: 64
                  not:
                    pattern: '[\r\n]'
                  description: |
                    Alphanumeric identifier for the user group, used to associate the embed user with a shared entity folder. See the [`entity` parameter reference](/embed/setup/url-parameters/entity) for more information.
                entityAiCreditLimit:
                  type: string
                  not:
                    pattern: '[\r\n]'
                  description: |
                    **Requires `entity`**. The AI credit cap for the specified `entity`. Send a non-negative number as a string, or `unlimited` (case-insensitive). An empty string is treated as omitted.
                    
                    See the [`entityAiCreditLimit` parameter reference](/embed/setup/url-parameters/entityAiCreditLimit) for more information.
                entityFolderContentRole:
                  type: string
                  not:
                    pattern: '[\r\n]'
                  enum:
                    - ''
                    - MANAGER
                    - EDITOR
                    - VIEWER
                    - NO_ACCESS
                  description: |
                    Content role for the embed user's shared entity folder. See the [`entityFolderContentRole` parameter reference](/embed/setup/url-parameters/entityFolderContentRole) for role definitions.
                entityFolderGroupContentRole:
                  type: string
                  default: "VIEWER"
                  not:
                    pattern: '[\r\n]'
                  enum:
                    - ''
                    - MANAGER
                    - EDITOR
                    - VIEWER
                    - NO_ACCESS
                  description: |
                    Content role for the entity group's shared entity folder. Defaults to `VIEWER` if unspecified. See the [`entityFolderGroupContentRole` parameter reference](/embed/setup/url-parameters/entityFolderGroupContentRole) for more information.
                entityFolderLabel:
                  type: string
                  maxLength: 64
                  not:
                    pattern: '[\r\n]'
                  description: |
                    **Requires `entity`.** Label for the embed user's entity folder. See the [`entityFolderLabel` parameter reference](/embed/setup/url-parameters/entityFolderLabel) for more information.
                entityGroupLabel:
                  type: string
                  maxLength: 64
                  not:
                    pattern: '[\r\n]'
                  description: |
                    **Requires `entity`.** Label for the embed entity group. See the [`entityGroupLabel` parameter reference](/embed/setup/url-parameters/entityGroupLabel) for more information.
                externalId:
                  type: string
                  minLength: 1
                  not:
                    pattern: '[\r\n]'
                  description: |
                    External identifier for the user, from your system. Cannot be empty. See the [`externalId` parameter reference](/embed/setup/url-parameters/externalId) for more information.
                  example: user-123
                filterSearchParam:
                  type: string
                  not:
                    pattern: '[\r\n]'
                  description: |
                    Filter URL search parameter to add to the `contentPath`. See the [`filterSearchParam` parameter reference](/embed/setup/url-parameters/filterSearchParam) for the value format.
                groups:
                  type: array
                  items:
                    type: string
                  description: Optional list of non-entity group names to assign to the user. Entity-group membership is managed by the [`entity` URL parameter](/embed/setup/url-parameters/entity).
                  example:
                    - engineering
                    - sales
                linkAccess:
                  type: string
                  not:
                    pattern: '[\r\n]'
                  description: Level of link access for the embed user. See the [`linkAccess` parameter reference](/embed/setup/url-parameters/linkAccess) for accepted values.
                mode:
                  type: string
                  not:
                    pattern: '[\r\n]'
                  enum:
                    - ''
                    - APPLICATION
                    - SINGLE_CONTENT
                  description: |
                    Access mode for the embed session:
                    
                    - `APPLICATION` enables [Create mode](/embed/customization/create-mode)
                    - `SINGLE_CONTENT` embeds a single dashboard, workbook, or app
                    
                    See the [`mode` parameter reference](/embed/setup/url-parameters/mode) for more information.
                modelRoles:
                  type: object
                  additionalProperties:
                    type: string
                  description: Map of model IDs to base or custom role names, overriding `connectionRoles`. Invalid or disallowed roles are ignored. See the [`modelRoles` parameter reference](/embed/setup/url-parameters/modelRoles) for more information.
                name:
                  type: string
                  minLength: 1
                  not:
                    pattern: '[\r\n]'
                  description: Display name for the user. Cannot be empty. See the [`name` parameter reference](/embed/setup/url-parameters/name) for more information.
                  example: Blob Ross
                nonce:
                  type: string
                  minLength: 32
                  maxLength: 32
                  not:
                    pattern: '[\r\n]'
                  description: Accepted for compatibility, but not used by this endpoint. Replay protection applies to signed-URL login instead. See the [`nonce` parameter reference](/embed/setup/url-parameters/nonce) for more information.
                preserveEntityFolderContentRole:
                  anyOf:
                    - type: string
                      enum:
                        - 'true'
                        - 'false'
                    - type: boolean
                  description: |
                    If `true`, retains the embed user's existing entity folder content role instead of applying the role from this request. See the [`preserveEntityFolderContentRole` parameter reference](/embed/setup/url-parameters/preserveEntityFolderContentRole) for more information.
                sdk:
                  type: string
                  maxLength: 64
                  description: Optional SDK telemetry marker used for Omni's internal tracking. Has no effect on the created session.
                signature:
                  type: string
                  minLength: 32
                  description: Accepted for compatibility, but this endpoint authenticates with an Organization API key, not this signature.
                timezone:
                  type: string
                  not:
                    pattern: '[\r\n]'
                  description: Accepted for compatibility, but this endpoint does not apply the timezone to the session. See the [`timezone` parameter reference](/embed/setup/url-parameters/timezone), which applies to signed-URL embed login instead.
                uiSettings:
                  type: object
                  properties:
                    showNavigation:
                      type: boolean
                      description: |
                        When `true`, the [sidebar](/administration/settings/sidebar) and icon will display in the top-left corner of the app. When `false`, the panel and icon are hidden. 
                    dashboardChartContextMenu:
                      type: boolean
                      description: |
                        When `true`, context menus (the <Icon icon="ellipsis-vertical" className="icons" /> icon and right-click menu) will be available on dashboard tiles. When `false`, context menus will be hidden.
                    documentBreadcrumb:
                      type: string
                      description: |
                        Controls the display of folder path breadcrumbs in document title headers. This setting applies to [Create mode embeds](/embed/customization/create-mode) and affects workbooks and dashboards.

                        Must be one of:

                        - `link` - Displays the folder path breadcrumb in the document title header as clickable links
                        - `display` - Displays the folder path breadcrumb as visible text without hyperlinks, preventing navigation out of the embed
                        - `none` - Hides the folder path breadcrumb entirely, including the personal-scope prefix (e.g., "X's Documents")
                      enum:
                        - display
                        - link
                        - none
                    dashboardGeneratedAt:
                      type: boolean
                      description: |
                        When `true`, the `Generated <timestamp>` stamp is displayed in the dashboard footer. When `false`, the timestamp is hidden. 
                    tileRanTimestamp:
                      type: boolean
                      description: |
                        When `true`, the `Query ran <timestamp>` entry is displayed in a tile's context menu.
                    showHeader:
                      type: boolean
                      description: |
                        When `true`, the top header bar is displayed across the app. When `false`, the header bar is hidden. This setting applies to dashboard, workbook, and application pages.
                    shareButton:
                      type: boolean
                      description: |
                        When `true`, users can share content, such as through the **Share** button. When `false`, all sharing options are hidden for the session.
                  description: |
                    Embed UI settings. Invalid values are ignored. See the [`uiSettings` parameter reference](/embed/setup/url-parameters/uiSettings) for more information.
                userAttributes:
                  type: object
                  description: |
                    Optional user attributes for row-level security. Values are a string, a number, or an array of strings/numbers. Numbers are converted to strings on receipt, so send anything beyond the JSON safe integer range (`2^53 - 1`) as a string or it loses precision before Omni sees it.
                    
                    See the [`userSettings` parameter reference](/embed/setup/url-parameters/userAttributes) for more information.
                  additionalProperties:
                    anyOf:
                      - type: string
                      - type: number
                        minimum: -1.7976931348623157e+308
                        maximum: 1.7976931348623157e+308
                      - type: array
                        items:
                          anyOf:
                            - type: string
                            - type: number
                              minimum: -1.7976931348623157e+308
                              maximum: 1.7976931348623157e+308
                  example:
                    account_id: '9007199254740993'
                    region: us-east
                    tier: 2
      responses:
        '200':
          description: Session token generated
          content:
            application/json:
              schema:  # EmbedSsoGenerateSessionResponse
                type: object
                required:
                  - membershipId
                  - sessionId
                  - userId
                properties:
                  membershipId:
                    type: string
                    description: Membership ID of the embed user the session logs in as. This is the same ID used by the [List](/api/users/list-embed-users) and [retrieve embed users](/api/users/retrieve-embed-user) endpoints.
                  sessionId:
                    type: string
                    description: Session ID to use for embedding Omni content
                  userId:
                    type: string
                    description: Omni user ID of the embed user the session logs in as. This ID is created on the user's first request with a given `externalId`, and reused on later requests.
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required (API key with embed scope)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Permission denied - embed not enabled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'

  /v1/folders:
    post:
      tags:
        - Folders
      summary: Create folder
      description: |
        Create a new folder. Folders can be nested up to 7 levels.

        Folder paths are automatically generated based on hierarchy. Child folder paths include the paths of their parents.
      security:
        - bearerAuth: []
      operationId: createFolder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  description: Name of the folder.
                parentFolderId:
                  type: string
                  format: uuid
                  description: |
                    ID of the parent folder. Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.

                    Maximum nesting depth is 7 levels. A `400` error is returned when the limit is reached.
                scope:
                  type: string
                  enum: [organization, restricted]
                  default: organization
                  description: |
                    Scope of the folder.

                    - If no scope is provided and no parent folder exists, defaults to `organization`
                    - If no scope is provided but a parent folder exists, inherits the parent folder's scope
                    - Child folder scope must match the parent folder's scope
                userId:
                  type: string
                  format: uuid
                  description: ID of the folder owner. Use the [List users](/api/users/list-users) or [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs.
                breadcrumbRoot:
                  type: boolean
                  description: |
                    When `true`, breadcrumbs for this folder and its descendants start at this folder, hiding ancestor folders and the scope crumb (`Shared`, `<name>'s Documents`).

                    This is useful for scoping navigation to a subtree, for example in embed use cases built around a specific `contentPath` folder.
      responses:
        '201':
          description: Folder created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: Unique identifier for the folder
                  name:
                    type: string
                    description: Display name of the folder
                  path:
                    type: string
                    description: Full path to the folder
                  scope:
                    type: string
                    enum: [organization, restricted]
                    description: |
                      Visibility scope of the folder
                  ownerId:
                    type: string
                    description: ID of the user that owns the folder
                  breadcrumbRoot:
                    type: boolean
                    description: Whether breadcrumbs start at this folder
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: Invalid JSON`
            - `Bad Request: name: Required`
            - `Bad Request: Maximum folder nesting depth reached`
            - `Bad Request: Child folder scope must match parent folder scope`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages/causes:

            - `User-scoped API keys cannot act on behalf of other users`
            - Operations targeting personal/restricted scope require the user's **Personal content access** setting (`omni_allows_personal_content` system attribute) to be enabled for the user. If not enabled, the API will respond with `403 Forbidden`.
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Parent folder with id <parentFolderId> does not exist`
            - `User with id <userId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - Folders
      summary: List folders
      description: |
        Retrieve a paginated list of folders within an organization. Supports filtering, sorting, and cursor-based pagination.

        The folders endpoint behaves differently based on the type of API key used to authenticate the request:

        - **Organization-scoped API keys:** Full access to all folders. Can view any user's restricted folders with `ownerId`. Must provide `ownerId` for `scope=restricted`.
        - **Personal Access Tokens (PAT):** Permission-filtered access matching the UI.
           - For `scope=organization` - Returns all organization-shared folders the user can access
           - For `scope=restricted` - Returns only the user's own restricted folders. An `ownerId` is optional; if not provided, it will be auto-inferred and the API will return only the folders belonging to PAT owner.
      security:
        - bearerAuth: []
      operationId: listFolders
      parameters:
        - name: include
          in: query
          schema:
            type: string
            pattern: ^\s*((_count|labels|onlyFavorites|onlySharedWithMe)\s*)?(,\s*((_count|labels|onlyFavorites|onlySharedWithMe)\s*)?)*$
          description: |
            Comma-separated list of additional fields to include:

            - `_count` - Include document and favorite counts
            - `labels` - Include folder labels
            - `onlyFavorites` - Returns only folders the user has favorited and can still access. Cannot be combined with `onlySharedWithMe`, `ownerId`, or `path`.
            - `onlySharedWithMe` - Returns only folders explicitly shared with the specified user via folder permissions. Excludes folders owned by the user. Cannot be combined with `onlyFavorites`, `ownerId`, or `path`.

               Additionally, depending on the type of API key being used:

               - **Organization API keys** require the `userId` parameter
               - **Personal Access Tokens** automatically infer the `userId` from the token
        - name: path
          in: query
          schema:
            type: string
          description: |
            Filter folders by path. Wildcards are supported and must appear at the end of the path:

            - `*` - Include direct children only (e.g., `blob-sales/*`)
            - `**` - Include all descendants recursively (e.g., `blob-sales/**`)
        - name: labels
          in: query
          schema:
            type: array
            items:
              type: string
          description: Comma-separated list of labels to filter by.
        - name: scope
          in: query
          schema:
            type: string
            enum: [organization, restricted]
            default: organization
          description: |
            Scope of folders to retrieve.

            - When `include=onlySharedWithMe` or `include=onlyFavorites` is specified without a `scope` parameter, returns results of all scopes with each folder's actual scope preserved
            - Otherwise, defaults to `organization`

            When set to `restricted`, the `ownerId` parameter behavior depends on the API key type:

            - **Organization-scoped API keys:** `ownerId` is required
            - **Personal Access Tokens (PAT):** `ownerId` is optional and auto-inferred to the token owner
        - name: sortField
          in: query
          schema:
            type: string
            enum: [favorites, name, path]
            default: name
          description: |
            Field to sort by:

            - `favorites` - Sort by number of favorites
            - `name` - Sort by folder name
            - `path` - Sort by folder path
        - name: sortDirection
          in: query
          schema:
            type: string
            enum: [asc, desc]
            default: desc
          description: Sort direction.
        - name: cursor
          in: query
          schema:
            type: string
          description: Cursor for pagination positioning.
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            default: 20
          description: Number of items per page.
        - name: ownerId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            UUID of organization membership.

            How the API behaves depends on the type of API key used to authenticate the request and the current `scope` value.

            **Personal Access Tokens (PAT)**

            | Scope | ownerId | Behavior |
            |-------|---------|----------|
            | `organization` | Not provided | All organization-shared folders user has permission to access |
            | `organization` | own ID | Own organization folders only |
            | `restricted` | Not provided | Infers owner's ID, returns own restricted folders |
            | `restricted` | Own ID | Own restricted folders |
            | `restricted` | Other's ID | 403 Forbidden |

            **Organization API key**

            | Scope | ownerId | Behavior |
            |-------|---------|----------|
            | `organization` | Not provided | All organization folders |
            | `organization` | Any ID | That user's organization folders |
            | `restricted` | Not provided | 400 Bad Request (`ownerId` required) |
            | `restricted` | Any ID | That user's restricted folders |
        - name: userId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            Membership ID of the user whose folders to return. Only used with `include=onlyFavorites` or `include=onlySharedWithMe`.

            Required when using an Organization API key with either flag. Automatically inferred from the token when using a Personal Access Token.
      responses:
        '200':
          description: Paginated folder list
          content:
            application/json:
              schema:
                type: object
                properties:
                  records:
                    type: array
                    description: Array of folder objects matching the query.
                    items:
                      $ref: '#/components/schemas/Folder'
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
              example:
                records:
                  - id: "21db26b3-466c-4791-90e7-b9ce9375426d"
                    name: "Blob Sales"
                    path: "/blob-sales"
                    scope: "organization"
                    url: "https://blobsrus.omni.co/f/blob-sales-reports"
                    owner:
                      id: "f4df8d6e-7f69-4d54-b23b-7abfe5c4da74"
                      name: "Blob Ross"
                    labels:
                      - "important"
                      - "archived"
                    _count:
                      documents: 15
                      favorites: 3
                pageInfo:
                  hasNextPage: true
                  nextCursor: "eyJpZCI6ImZvbGRlcjEyMyJ9"
                  pageSize: 20
                  totalRecords: 45
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: pageSize: Page size must be at least 1`
            - `Bad Request: ownerId: ownerId is required when scope is 'restricted'` (organization-scoped API keys only)
            - `Bad Request: sortField: Invalid enum value. Expected 'name' | 'path', received '<invalidField>'`
            - `Bad Request: include: Invalid value. Expected: _count, labels, onlyFavorites, onlySharedWithMe, received '<invalidValue>'`
            - `Bad Request: Invalid path pattern. Only a single wildcard (*) is allowed at the end of the pattern`
            - `Bad Request: onlyFavorites requires userId parameter for org-scoped API keys` - When using `include=onlyFavorites` with an Organization API key without providing a `userId` parameter
            - `Bad Request: onlyFavorites cannot be combined with ownerId` - When using `include=onlyFavorites` with an `ownerId` parameter
            - `Bad Request: onlyFavorites cannot be combined with path` - When using `include=onlyFavorites` with a `path` parameter
            - `Bad Request: onlySharedWithMe cannot be combined with onlyFavorites` - When using `include=onlySharedWithMe` together with `include=onlyFavorites`
            - `Bad Request: onlySharedWithMe requires userId` - When using `include=onlySharedWithMe` with an Organization API key without providing a `userId` parameter
            - `Bad Request: onlySharedWithMe cannot be combined with ownerId` - When using `include=onlySharedWithMe` with an `ownerId` parameter
            - `Bad Request: onlySharedWithMe cannot be combined with path` - When using `include=onlySharedWithMe` with a `path` parameter
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages/causes:
            
            - Personal Access Tokens (PAT) cannot access other users' restricted folders.
            - `User-scoped API keys cannot act on behalf of other users`
            - Operations targeting personal/restricted scope require the user's **Personal content access** setting (`omni_allows_personal_content` system attribute) to be enabled for the user. If not enabled, the API will respond with `403 Forbidden`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Folder with path <path> does not exist`
            - `User membership not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/folders/{folderId}:
    get:
      tags:
        - Folders
      summary: Get folder
      description: |
        Retrieve a single folder by its ID. Owner and permission information is returned separately by the [Get folder permissions](/api/folder-permissions/get-folder-permissions) endpoint.

        The calling user must have access to the specified folder or the API will return an error. 
      security:
        - bearerAuth: []
      operationId: getFolder
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the folder to retrieve. Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: The requested folder
          content:
            application/json:
              schema:  # FoldersGetResponse
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: Unique identifier for the folder
                  name:
                    type: string
                    description: Display name of the folder
                  path:
                    type: string
                    description: Full path to the folder, including parent folders
                  url:
                    type: string
                    description: URL to view the folder in Omni
                  breadcrumbRoot:
                    type: boolean
                    description: Whether breadcrumbs for content in this folder start here
                  labels:
                    type: array
                    items:
                      type: string
                    description: Labels applied to the folder
                required:
                  - id
                  - name
                  - path
                  - url
                  - breadcrumbRoot
                  - labels
              examples:
                success:
                  summary: Folder found
                  value:
                    id: "3c90c3cc-0d44-4b50-8888-8dd25736052a"
                    name: "Q1 Reports"
                    path: "sales-reports/q1-reports"
                    url: "https://{instance}.omniapp.co/f/sales-reports/q1-reports"
                    breadcrumbRoot: false
                    labels:
                      - "finance"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `folderId` is not a valid UUID
            - `userId` is not a valid UUID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - A personal access token named a `userId` other than its own
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Folder with id <folderId> does not exist` - Also returned when the folder exists but the caller, or the user named by `userId`, cannot view it
            - `userId` does not match a member of the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    patch:
      tags:
        - Folders
      summary: Update folder
      description: |
        <Note>
          This endpoint requires [**Editor** permissions or higher](/share#content-access-permissions) on the folder being updated.
        </Note>

        Update a folder's name and/or path segment. Either or both fields can be updated in a single request.
      x-mint:
        content: |
          Name-only updates will not trigger updates to descendant folders (subfolders).

          However, path updates will automatically update all descendent folder paths. Consider the following folder structure:

          ```text title="Existing paths with /blob-shared"
          .
          └── blob-shared
              ├── sales      # full path: /blob-shared/sales
              └── marketing  # full path: /blob-shared/marketing
          ```

          If the path for `blob-shared` is updated to `all-blobs`, the full paths for its descendent folders will be automatically updated:

          ```text title="New paths with /all-blobs"
          .
          └── all-blobs
              ├── sales       # full path: /all-blobs/sales
              └── marketing   # full path: /all-blobs/marketing
          ```
      security:
        - bearerAuth: []
      operationId: updateFolder
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the folder to update. Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: |
                    The new display name for the folder. Leading and trailing whitespace is automatically trimmed.

                    At least one of `name` or `path` must be provided.
                path:
                  type: string
                  description: |
                    The new URL path segment for the folder, which will automatically be converted to lowercase. When a folder's path is updated, all descendant folder paths are automatically updated.

                    To be valid, path segments:

                    - Can contain only alphanumeric characters and dashes
                    - Cannot use reserved names: `favorite`, `labels`, `move`, `share`, `transfer`. Folder names may include these words, but the name cannot be only this word. For example, `favorite` is invalid but `my-favorite` is not.

                    At least one of `name` or `path` must be provided.
                resolvePathConflict:
                  type: boolean
                  default: false
                  description: |
                    When `true`, automatically resolves path conflicts by appending a numeric suffix (e.g., `blob-sales-2`).

                    When `false` (default), returns a `409 Conflict` error if the path already exists.
                breadcrumbRoot:
                  type: boolean
                  description: |
                    When `true`, breadcrumbs for this folder and its descendants start at this folder, hiding ancestor folders and the scope crumb (`Shared`, `<name>'s Documents`)

                    Omitting this field leaves the current setting unchanged. Setting it to `false` or `null` explicitly clears the breadcrumb root setting.
              example:
                name: "Blob Sales Reports"
                path: "sales-reports"
                resolvePathConflict: "true"
      responses:
        '200':
          description: Folder updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: Unique identifier for the folder
                  name:
                    type: string
                    description: Display name of the folder
                  path:
                    type: string
                    description: Full path to the folder
                  breadcrumbRoot:
                    type: boolean
                    description: Whether breadcrumbs start at this folder
              examples:
                success:
                  summary: Successful update
                  value:
                    id: "3c90c3cc-0d44-4b50-8888-8dd25736052a"
                    name: "Blob Sales Reports"
                    path: "sales-reports"
                    breadcrumbRoot: false
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `At least one of 'name' or 'path' must be provided`
            - `Path cannot be one of the reserved names: favorite, labels, move, share, transfer`
            - `Path must contain only alphanumeric characters and dashes`
            - Unrecognized field in the request body (e.g., a `labels` field)
            - Invalid JSON or validation errors
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missingFields:
                  summary: No fields provided
                  value:
                    error: "400"
                    message: "At least one of 'name' or 'path' must be provided"
                reservedPath:
                  summary: Reserved path name
                  value:
                    error: "400"
                    message: "Path cannot be one of the reserved names: favorite, labels, move, share, transfer"
                invalidPathCharacters:
                  summary: Invalid path characters
                  value:
                    error: "400"
                    message: "Path must contain only alphanumeric characters and dashes"
        '403':
          description: |
            Forbidden

            Possible error messages:

            - Permission denied. Authenticating user must have **Edit** permissions on the folder.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Folder with id <folderId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: |
            Conflict

            Returned when `resolvePathConflict=false` (default) and the requested path already exists in the parent folder.

            Possible error messages:

            - `A folder with path '<path>' already exists at this location`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                pathConflict:
                  summary: Path already exists
                  value:
                    error: "409"
                    message: "A folder with path 'sales-reports' already exists at this location"
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Folders
      summary: Delete folder
      description: |
        Delete a folder. By default, only empty folders can be deleted. Use the `force` parameter to recursively delete folders containing documents and sub-folders.

        When `force` is enabled:

        - All documents are archived (soft-delete to trash) across the folder tree
        - Subfolders are deleted recursively, starting with the most deeply-nested subfolder and walking up the folder tree
        - Up to 100 items (documents and subfolders) can be deleted. If a folder contains more than 100 items, the item will return a `400`. No items will be deleted.
        - All operations occur in a single transaction - partial failures roll back completely
        - SSO embed users cannot force-delete entity folders
      security:
        - bearerAuth: []
      operationId: deleteFolder
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the folder to delete. Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.
        - name: force
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: When `true`, recursively archives documents (to trash) and deletes sub-folders. Limited to 100 total items.
      responses:
        '200':
          description: Folder deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Folders with documents cannot be deleted` (when `force=false` and folder is not empty)
            - `Only empty folders can be deleted` (when `force=false` and folder is not empty)
            - `Folder contains too many items to force delete. Found <count> items (limit: 100)` (when `force=true` and folder tree exceeds 100 items)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - Permission denied (when user lacks delete permission)
            - SSO embed users cannot force-delete entity folders (when SSO embed user attempts to force-delete entity folder)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Folder with id <folderId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/folders/{folderId}/labels:
    patch:
      tags:
        - Folder labels
      summary: Bulk update folder labels
      description: Add and/or remove multiple labels from a folder.
      x-mint:
        content: |
          When using this endpoint, keep in mind that:

          - **All changes succeed or fail together.** No partial updates occur.
          - **Label matching is case-insensitive**.
          - **Requests must have at least one operation.** Either `add` or `remove` must contain at least one label.
          - **Labels must already exist to be added to or removed from a folder.** Create labels with the [Create label API](/api/labels/create-label).
          - **Labels cannot be included in both `add` and `remove` in the same request.**
          - **Organization Admin permissions** are required to add or remove **Verified** or **Homepage** labels
      security:
        - bearerAuth: []
      operationId: bulkUpdateFolderLabels
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The folder identifier (UUID). Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Optional user ID that attributes the action to the specified user.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                add:
                  type: array
                  items:
                    type: string
                    minLength: 2
                    maxLength: 25
                  default: []
                  description: Label names to add to the folder
                remove:
                  type: array
                  items:
                    type: string
                    minLength: 2
                    maxLength: 25
                  default: []
                  description: Label names to remove from the folder
              additionalProperties: false
            examples:
              addLabels:
                summary: Add labels
                value:
                  add:
                    - production
                    - reviewed
              removeLabels:
                summary: Remove labels
                value:
                  remove:
                    - draft
                    - needs-review
              addAndRemove:
                summary: Add and remove labels
                value:
                  add:
                    - approved
                  remove:
                    - pending-review
      responses:
        '200':
          description: Labels updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  labels:
                    type: array
                    items:
                      type: string
                    description: The updated list of labels on the folder
              example:
                labels:
                  - label-one
                  - label-two
                  - new-label
        '400':
          description: |
            Bad Request. Possible causes:

            - Empty request - Neither `add` nor `remove` contains any labels
            - Label appears in both `add` and `remove` arrays
            - Invalid label name (less than 2 or more than 25 characters)
            - Unrecognized field in the request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                emptyRequest:
                  summary: Empty request
                  value:
                    detail: "Bad Request: At least one label must be specified in add or remove"
                    status: 400
                    title: Bad Request
                conflictingLabels:
                  summary: Label in both arrays
                  value:
                    detail: "Bad Request: Labels cannot appear in both add and remove arrays"
                    status: 400
                    title: Bad Request
                unrecognizedField:
                  summary: Unrecognized field
                  value:
                    detail: "Bad Request: Unrecognized field in request body"
                    status: 400
                    title: Bad Request
        '403':
          description: |
            Forbidden. Possible causes:

            - User does not have permissions on the folder to add labels (Manager or Owner permissions on the folder)
            - User lacks Organization Admin permissions to modify **Verified** labels
            - User lacks Organization Admin permissions to modify **Homepage** labels
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                permissionDenied:
                  summary: Permission denied
                  value:
                    detail: You do not have permission to modify labels on this folder.
                    status: 403
                    title: Forbidden
                verifiedLabelDenied:
                  summary: Verified label permission denied
                  value:
                    detail: You do not have permission to modify Verified labels on this folder
                    status: 403
                    title: Forbidden
                homepageLabelDenied:
                  summary: Homepage label permission denied
                  value:
                    detail: Organization Admin permissions required to modify Homepage labels
                    status: 403
                    title: Forbidden
        '404':
          description: |
            Not Found. Possible causes:

            - Folder does not exist
            - Label does not exist. Create the label first with the [Create label API](/api/labels/create-label).
            - Label in `remove` array does not exist on the folder
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                folderNotFound:
                  summary: Folder not found
                  value:
                    detail: Folder with id "abc123" not found
                    status: 404
                    title: Not Found
                labelNotFound:
                  summary: Label not found globally
                  value:
                    detail: Label "my-label" not found
                    status: 404
                    title: Not Found
                labelNotOnFolder:
                  summary: Label not on folder
                  value:
                    detail: |
                      Labels not found on this folder: "label-one", "label-two"
                    status: 404
                    title: Not Found
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/folders/{folderId}/favorite:
    put:
      tags:
        - Folder favorites
      summary: Favorite folder
      description: |
        Add a folder to a user's favorites. Permissions are not checked; note that favoriting a folder using this endpoint does not grant access.

        Successful requests will return `204` regardless of whether the folder is newly favorited or already in the user's favorites.
      security:
        - bearerAuth: []
      operationId: foldersAddFavorite
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
          description: Unique identifier for the folder.
        - $ref: '#/components/parameters/userId'
      responses:
        '204':
          description: |
            Folder favorited successfully. No response body.

            This response is returned whether the folder was newly favorited or already in favorites.
        '400':
          description: |
            Bad Request. Possible causes:

            - `folderId` is not a valid UUID
            - `userId` is not a valid UUID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden. Possible causes:

            - Personal Access Token attempted to act on behalf of another user. PATs cannot use the `userId` parameter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible causes:

            - Folder does not exist in the organization
            - Specified `userId` not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Folder favorites
      summary: Unfavorite folder
      description: |
        Remove a folder from a user's favorites.

        Successful requests will return `204` regardless of whether the folder was previously favorited or not.
      security:
        - bearerAuth: []
      operationId: foldersRemoveFavorite
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
          description: Unique identifier for the folder.
        - $ref: '#/components/parameters/userId'
      responses:
        '204':
          description: |
            Folder unfavorited successfully. No response body.

            This response is returned whether the folder was previously favorited or not.
        '400':
          description: |
            Bad Request. Possible causes:

            - `folderId` is not a valid UUID
            - `userId` is not a valid UUID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden. Possible causes:

            - Personal Access Token attempted to act on behalf of another user. PATs cannot use the `userId` parameter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible causes:

            - Folder does not exist in the organization
            - Specified `userId` not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/folders/{folderId}/permissions:
    post:
      tags:
        - Folder permissions
      summary: Grant folder permissions
      description: Grant folder permissions to users or groups
      security:
        - bearerAuth: []
      operationId: grantFolderPermissions
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the folder. Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - role
              properties:
                role:
                  type: string
                  enum: [NO_ACCESS, VIEWER, EDITOR, MANAGER, OWNER]
                  description: |
                    The content role to assign to the specified users or user groups. Must be one of:
                    - `NO_ACCESS` - No access
                    - `VIEWER` - View folder contents
                    - `EDITOR` - Edit and add content to the folder
                    - `MANAGER` - Edit, add content, and manage folder permissions
                    - `OWNER` - Manager permissions and receives access requests. The document must have at least one owner at all times.
                  example: "EDITOR"
                accessBoost:
                  type: boolean
                  default: false
                  description: |
                    If `true`, [AccessBoost](/share#boosting-permissions-with-accessboost) is enabled for the folder. Setting this to `false` is always allowed. Setting this to `true` with a user-scoped API key requires that the organization's **Non-administrators can enable AccessBoost on content** setting is enabled and that the user has permission to enable AccessBoost, or the request returns a 403 error. Organization-scoped API keys are not subject to this check.
                  example: false
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: |
                    The list of user IDs to assign permissions to. Use the [List users](/api/users/list-users) and [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs. **Either `userIds` or `userGroupIds` is required.**
                  example: ["3c90c3cc-0d44-4b50-8888-8dd25736052a"]
                userGroupIds:
                  type: array
                  items:
                    type: string
                  description: |
                    The list of user group IDs to assign permissions to. Use the [List user groups](/api/user-groups/list-user-groups) endpoint to retrieve user group IDs. **Either `userIds` or `userGroupIds` is required.**
                  example: ["mEhXj6ZI"]
      responses:
        '200':
          description: Permissions granted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `userIds.userGroupIds: userIds or userGroupIds must be provided`
            - `userIds.0: Invalid uuid`
            - `userGroupIds.0: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `User does not have permission to manage folder permissions` - The user sending the API request must have **Manager** permissions for the folder.
            - `accessBoost` is set to `true` with a user-scoped API key, but the organization's **Non-administrators can enable AccessBoost on content** setting is disabled, or the user does not have permission to enable AccessBoost.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Folder with identifier "<folderId>" not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - Folder permissions
      summary: Update organization-level folder permissions
      description: |
        Set the role every member of the organization holds on a folder, and/or turn [AccessBoost](/share#boosting-permissions-with-accessboost) on or off for that organization-wide access, without naming individual users or groups. The role cascades to every descendant folder and document, so a single call can re-permission an entire folder tree.

        At least one of `organizationRole` or `organizationAccessBoost` is required.
      security:
        - bearerAuth: []
      operationId: foldersUpdatePermissionSettings
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the folder. Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                organizationAccessBoost:
                  type: boolean
                  description: |
                    Whether everyone in the organization gets [AccessBoost](/share#boosting-permissions-with-accessboost) on this folder. Setting this to `true` requires an organization role that grants access, either sent in the same request or already on the folder; otherwise the request is rejected.
                organizationRole:
                  description: |
                    The role every member of the organization holds on the folder. Must be one of `NO_ACCESS`, `VIEWER`, `EDITOR`, or `MANAGER`. `OWNER` cannot be granted at the organization level. Pass `null` to clear the organization role.
                  type: string
                  enum: [NO_ACCESS, VIEWER, EDITOR, MANAGER, OWNER]
              additionalProperties: false
              minProperties: 1
            examples:
              setOrganizationRole:
                summary: Set the organization role
                value:
                  organizationRole: VIEWER
              clearOrganizationRole:
                summary: Clear the organization role
                value:
                  organizationRole: null
              enableAccessBoost:
                summary: Enable AccessBoost for the organization's access
                value:
                  organizationAccessBoost: true
      responses:
        '200':
          description: Permission settings updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Whether the permission settings were updated successfully
                required:
                  - success
        '400':
          description: |
            Bad Request

            Possible causes:

            - Neither `organizationRole` nor `organizationAccessBoost` was provided, or an unrecognized field was included
            - `organizationRole` was set to `OWNER`, which cannot be granted at the organization level
            - `organizationAccessBoost: true` was combined with a role that grants no access — either the one in the request, or the folder's current role when the request omits `organizationRole`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible causes:

            - `User does not have permission to manage folder permissions` - The user sending the API request must have **Manager** permissions for the folder.
            - AccessBoost is turned off for the organization
            - A user-scoped key lacks boost provisioning
            - The organization's **Organization access for personal content** setting is off, the folder is in a personal space (My Documents), and `organizationRole` would raise the Organization role above what the folder already holds, directly or from a parent folder. The error message is `Your organization does not allow sharing personal content with the whole organization. Move it to a shared space first.`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Folder with identifier "<folderId>" not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'

    patch:
      tags:
        - Folder permissions
      summary: Update folder permissions
      description: Update existing folder permissions for users or groups
      security:
        - bearerAuth: []
      operationId: updateFolderPermissions
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the folder. Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - role
              properties:
                role:
                  type: string
                  enum: [NO_ACCESS, VIEWER, EDITOR, MANAGER, OWNER]
                  description: |
                    The content role to assign to the specified users or user groups. Must be one of:
                    - `NO_ACCESS` - No access
                    - `VIEWER` - View folder contents
                    - `EDITOR` - Edit and add content to the folder
                    - `MANAGER` - Edit, add content, and manage folder permissions
                    - `OWNER` - Manager permissions and receives access requests. The document must have at least one owner at all times.
                  example: "EDITOR"
                accessBoost:
                  type: boolean
                  default: false
                  description: |
                    If `true`, [AccessBoost](/share#boosting-permissions-with-accessboost) is enabled for the folder. Setting this to `false` is always allowed. Setting this to `true` with a user-scoped API key requires that the organization's **Non-administrators can enable AccessBoost on content** setting is enabled and that the user has permission to enable AccessBoost, or the request returns a 403 error. Organization-scoped API keys are not subject to this check.
                  example: false
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: |
                    The list of user IDs to assign permissions to. Use the [List users](/api/users/list-users) and [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs. **Either `userIds` or `userGroupIds` is required.**
                  example: ["3c90c3cc-0d44-4b50-8888-8dd25736052a"]
                userGroupIds:
                  type: array
                  items:
                    type: string
                  description: |
                    The list of user group IDs to assign permissions to. Use the [List user groups](/api/user-groups/list-user-groups) endpoint to retrieve user group IDs. **Either `userIds` or `userGroupIds` is required.**
                  example: ["mEhXj6ZI"]
      responses:
        '200':
          description: Permissions updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `userIds.userGroupIds: userIds or userGroupIds must be provided`
            - `userIds.0: Invalid uuid`
            - `userGroupIds.0: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `User does not have permission to manage folder permissions` - The user sending the API request must have **Manager** permissions for the folder.
            - `accessBoost` is set to `true` with a user-scoped API key, but the organization's **Non-administrators can enable AccessBoost on content** setting is disabled, or the user does not have permission to enable AccessBoost.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Folder with identifier "<folderId>" not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - Folder permissions
      summary: Get folder permissions
      description: |
        Retrieve the permissions on a folder. 
      security:
        - bearerAuth: []
      operationId: getFolderPermissions
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the folder. Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.
        - name: userId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: |
            The ID of the user to return folder permissions for. When omitted, the response lists all user and user group permissions on the folder. Use the [List users](/api/users/list-users) or [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs.
      responses:
        '200':
          description: Folder permissions object
          content:
            application/json:
              schema:
                type: object
                properties:
                  permits:
                    type: array
                    items:
                      $ref: '#/components/schemas/FolderPermission'
              example:
                permits:
                  - id: "b2c3d4e5-f6a7-8901-bcde-f23456789012"
                    userId: "b2c3d4e5-f6a7-8901-bcde-f23456789012"
                    name: "Jane Smith"
                    description: "jane@example.com"
                    type: "user"
                    direct:
                      accessBoost: false
                      isOwner: true
                      role: "OWNER"
                  - id: "c3d4e5f6-a7b8-9012-cdef-345678901234"
                    name: "Jane Smith"
                    description: "Organization - via Folder"
                    type: "user"
                    direct:
                      accessBoost: false
                      isOwner: false
                      role: "VIEWER"
                  - id: "mEhXj6ZI"
                    userGroupId: "mEhXj6ZI"
                    name: "Data Analysts"
                    type: "userGroup"
                    direct:
                      accessBoost: false
                      isOwner: false
                      role: "EDITOR"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `userId: userId must be a valid UUID`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `User does not have permission to view folder permissions` - The request includes `userId`, and the user sending the API request must have at least **Viewer** permissions for the folder.
            - `User does not have permission to list all folder permissions` - The request omits `userId`, and the user sending the API request must have **Manager** permissions for the folder.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Folder with identifier "<folderId>" not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Folder permissions
      summary: Revoke folder permissions
      description: Revoke folder permissions from users or groups
      security:
        - bearerAuth: []
      operationId: revokeFolderPermissions
      parameters:
        - name: folderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The UUID of the folder. Use the [List folders](/api/folders/list-folders) endpoint to retrieve folder IDs.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: |
                    The list of user IDs to revoke permissions from. Use the [List users](/api/users/list-users) or [List embed users](/api/users/list-embed-users) endpoints to retrieve user IDs.

                    Either `userIds` or `userGroupIds` is required.
                userGroupIds:
                  type: array
                  items:
                    type: string
                  description: |
                    The list of user group IDs to revoke permissions from. Use the [List user groups](/api/user-groups/list-user-groups) endpoint to retrieve user group IDs.

                    Either `userIds` or `userGroupIds` is required.
      responses:
        '200':
          description: Permissions revoked successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `userIds.userGroupIds: userIds or userGroupIds must be provided`
            - `userIds.0: Invalid uuid`
            - `userGroupIds.0: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `User does not have permission to manage folder permissions` - The user sending the API request must have **Manager** permissions for the folder.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Folder with identifier "<folderId>" not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/jobs/{jobId}/status:
    get:
      tags:
        - Jobs
      summary: Get job status
      description: |
        <Note>
          Currently, this endpoint only supports schema refresh jobs. Job IDs from other job types will return an error.
        </Note>
        
        Retrieves the current status of an asynchronous job. The user authenticating the request must have **read** permissions on the connection.

        This endpoint is used to check the status of jobs initiated by other API calls, such as schema refreshes. Poll this endpoint to determine when the job has completed.
      x-mint:
        content: |
          The response will contain one of the following statuses:
  
          | Status      | Description             |
          |-------------|-------------------------|
          | `RUNNING`   | Job is currently executing |
          | `COMPLETED` | Job finished successfully |
          | `FAILED`    | Job failed              |
  
          <Tip>
            We recommend the following when polling:
  
            - Use a reasonable polling interval (2-5 seconds)
            - Avoid polling more frequently than once per second
          </Tip>
      security:
        - bearerAuth: []
      operationId: getJobStatus
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The job ID returned from an asynchronous operation such as [Refresh schema](/api/models/refresh-schema)
      responses:
        '200':
          description: Job status retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_type:
                    type: string
                    description: The type of job
                    example: "refresh_schema"
                  job_id:
                    type: string
                    format: uuid
                    description: The unique identifier of the job
                  status:
                    type: string
                    enum: [RUNNING, COMPLETED, FAILED]
                    description: Current status of the job
              examples:
                running:
                  summary: Job running
                  value:
                    job_type: "refresh_schema"
                    job_id: "4e6953a9-a71b-4c0b-8b63-a9ea308f6aaf"
                    status: "RUNNING"
                completed:
                  summary: Job completed
                  value:
                    job_type: "refresh_schema"
                    job_id: "4e6953a9-a71b-4c0b-8b63-a9ea308f6aaf"
                    status: "COMPLETED"
                failed:
                  summary: Job failed
                  value:
                    job_type: "refresh_schema"
                    job_id: "4e6953a9-a71b-4c0b-8b63-a9ea308f6aaf"
                    status: "FAILED"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: jobId: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Job type not supported for status checks`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Job not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/labels:
    post:
      tags:
        - Labels
      summary: Create label
      description: |
        Create a new label in the organization.

        Any user can create basic labels, but **Organization Admin** permissions are required to:

        - Create **Verified** labels (`verified: true`)
        - Create **Homepage** labels (`homepage: true`)
      security:
        - bearerAuth: []
      operationId: createLabel
      parameters:
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Optional user ID that attributes the action to the specified user.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  minLength: 2
                  maxLength: 25
                  description: |
                    Label name. Must be 2-25 characters.

                    Names are case-insensitive: `"Production"` and `"production"` are considered the same.
                color:
                  type: string
                  maxLength: 9
                  default: null
                  description: |
                    Hex color for the label
                  example: "#0366d6"
                description:
                  type: string
                  maxLength: 500
                  default: null
                  description: Description of the label
                  example: "Documents based on dev schemas"
                verified:
                  type: boolean
                  default: false
                  description: |
                    **Requires Organization Admin permissions**. If `true`, documents with this label will be marked as verified/curated.
                homepage:
                  type: boolean
                  default: false
                  description: |
                    **Requires Organization Admin permissions**. If `true`, documents with this label will display on the instance's Homepage.
            example:
              name: "Dev"
              color: "#0366d6"
              description: "Documents based on dev schemas"
      responses:
        '201':
          description: Label created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Label'
              example:
                name: "Dev"
                verified: true
                homepage: true
                usage_count: 0
                color: "#0366d6"
                description: "Documents based on dev schemas"
        '400':
          description: |
            Bad Request. Possible causes:

            - Label name too short (less than 2 characters)
            - Label name too long (more than 25 characters)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden. Possible causes:

            - User lacks **Organization Admin** permissions, which are required to create Verified and Homepage labels
            - User lacks the permissions required to manage labels
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: |
            Conflict. A label with this name already exists (case-insensitive).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - Labels
      summary: List labels
      description: Retrieve all labels in the organization.
      security:
        - bearerAuth: []
      operationId: listLabels
      responses:
        '200':
          description: List of all labels in the organization
          content:
            application/json:
              schema:
                type: object
                properties:
                  labels:
                    type: array
                    items:
                      $ref: '#/components/schemas/Label'
              example:
                labels:
                  - name: "Production"
                    verified: true
                    homepage: true
                    usage_count: 12
                    color: "#000000"
                    description: "Documents verified and in prod"
                  - name: "In Review"
                    verified: true
                    homepage: false
                    usage_count: 5
                    color: "#CCCCCC"
                    description: "Documents in review"
                  - name: "Draft"
                    verified: false
                    homepage: false
                    usage_count: 3
                    color: "#EEEEEE"
                    description: "Drafts that need review"
        '429':
          $ref: '#/components/responses/TooManyRequests'
  
  /v1/labels/{labelName}:
    get:
      tags:
        - Labels
      summary: Get label
      description: Retrieve a single label by name.
      security:
        - bearerAuth: []
      operationId: getLabel
      parameters:
        - name: labelName
          in: path
          required: true
          schema:
            type: string
          description: |
            The label name. Lookup is case-insensitive.
            
            URL-encode special characters (e.g., `In%20Review` for "In Review").
      responses:
        '200':
          description: Label details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Label'
              example:
                name: "Production"
                verified: true
                homepage: true
                usage_count: 12
                color: "#000000"
                description: "Documents verified and in prod"
        '404':
          description: Label not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - Labels
      summary: Update label
      description: |
        Update an existing label, including renaming, changing **Verified** status, or adding/removing it from the **Homepage**.

        Users can update basic labels they created. **Organization Admin** permissions are required to modify **Verified** or **Homepage** labels, including making a label `verified` or visible on the `homepage`.
      security:
        - bearerAuth: []
      operationId: updateLabel
      parameters:
        - name: name
          in: path
          required: true
          schema:
            type: string
          description: |
            The name of the label to update. Lookup is case-insensitive.
            
            URL-encode special characters.
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
           **Requires an Organization API key**. Optional user ID that attributes the action to the specified user.
      requestBody:
        description: |
          Only include the fields you want to update when submitting requests.
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 2
                  maxLength: 25
                  description: The new name of the label.
                verified:
                  type: boolean
                  description: |
                    **Organization Admin permissions required**. If `true`, documents with the label will be marked as **Verified**.
                homepage:
                  type: boolean
                  description: |
                    **Organization Admin permissions required**. If `true`, documents with the label will be visible on the **Homepage**.
                color:
                  type: string
                  maxLength: 9
                  default: null
                  description: |
                    Hex color for the label
                  example: "#0366d6"
                description:
                  type: string
                  maxLength: 500
                  default: null
                  description: Description of the label
                  example: "Documents based on dev schemas"
            example:
              name: "Ready for Review"
              color: "#DDDDDD"
              description: "Documents that are ready for review"
      responses:
        '200':
          description: Label updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Label'
              example:
                name: "Ready for Review"
                verified: true
                homepage: false
                usage_count: 3
                color: "#DDDDDD"
                description: "Documents that are ready for review"
        '400':
          description: |
            Bad Request. Possible causes:

            - Name too short or too long
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden. Possible causes:

            - User lacks permission to manage labels
            - User lacks **Organization Admin** permissions, which are required to modify **Verified** labels
            - User lacks **Organization Admin** permissions, which are required to modify **Homepage** labels
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Label not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: |
            Conflict. The new label name already exists (case-insensitive).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      x-codeSamples:
        - lang: bash
          label: Rename label
          source: |
            curl -X PUT "https://{instance}.omniapp.co/api/v1/labels/Draft" \
              -H "Authorization: Bearer YOUR_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"name": "Ready for Review"}'
        - lang: bash
          label: Set verified (admin)
          source: |
            curl -X PUT "https://{instance}.omniapp.co/api/v1/labels/Production" \
              -H "Authorization: Bearer YOUR_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"verified": true, "homepage": true}'
    delete:
      tags:
        - Labels
      summary: Delete label
      x-mint:
        content: |
          <Warning>
            Labels that are currently applied to documents cannot be deleted. You must first remove the label from all documents.
          </Warning>
      description: |
        Delete a label from the organization.

        Any user can delete basic labels, but **Organization Admin** permissions are required to delete **Verified** and **Homepage** labels.
      security:
        - bearerAuth: []
      operationId: deleteLabel
      parameters:
        - name: name
          in: path
          required: true
          schema:
            type: string
          description: |
            The name of the label to delete. Lookup is case-insensitive.
            
            URL-encode special characters.
        - name: userId
          in: query
          required: false
          schema:
            type: string
          description: |
            **Requires an Organization API key**. Optional user ID that attributes the action to the specified user.
      responses:
        '204':
          description: Label deleted successfully. No response body.
        '403':
          description: |
            Forbidden. Possible causes:

            - User lacks permission to manage labels
            - User lacks **Organization Admin** permissions, which are required to delete **Verified** labels
            - User lacks **Organization Admin** permissions, which are required to delete **Homepage** labels
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Label not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: |
            Conflict. The label is currently applied to documents and cannot be deleted.

            Remove the label from all documents first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/branches:
    get:
      tags:
        - Branches
      summary: List branches
      description: |
        List the branches the caller may see: 
        
        - Branches they created that don't have changes
        - Branches with a model change on a model they can use branches on
        - Branches with a draft they can edit in a branch
        - Branches [open to viewers](/content/develop/branch-mode#opening-a-branch-to-viewers) that have a layer on a model the caller can read
      security:
        - bearerAuth: []
      operationId: listBranches
      parameters:
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/pageSize'
        - name: sortField
          in: query
          required: false
          schema:
            type: string
            enum: [updatedAt, createdAt, name]
            default: updatedAt
          description: Field to sort by.
        - $ref: '#/components/parameters/sortDirection'
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: One page of the branches the caller may see, most recently updated first by default.
          content:
            application/json:
              schema:
                type: object
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    items:
                      $ref: '#/components/schemas/BranchSummary'
                required:
                  - pageInfo
                  - records
              example:
                pageInfo:
                  hasNextPage: false
                  nextCursor: null
                  pageSize: 20
                  totalRecords: 1
                records:
                  - id: "123e4567-e89b-12d3-a456-426614174001"
                    name: "feature/new-metrics"
                    description: null
                    openToViewers: false
                    createdAt: "2026-01-15T10:00:00.000Z"
                    updatedAt: "2026-01-20T14:30:00.000Z"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `cursor`, `pageSize`, `sortField`, or `sortDirection` is invalid
            - An Organization API key was used without a `userId`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: A Personal Access Token was used with a `userId` naming a different member.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/branches/{branchId}:
    get:
      tags:
        - Branches
      summary: Get a branch
      description: |
        Get a branch and its parts, as the caller sees them. Each branch part includes whether the caller may merge it, and a reason code when they may not. The response only includes branch parts that the caller can see.
      security:
        - bearerAuth: []
      operationId: getBranch
      parameters:
        - $ref: '#/components/parameters/branchIdPath'
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: The branch and the parts the caller may see.
          content:
            application/json:
              schema:
                type: object
                properties:
                  branch:
                    allOf:
                      - $ref: '#/components/schemas/BranchSummary'
                      - type: object
                        properties:
                          parts:
                            type: array
                            description: The branch's parts, each either a changed model (which contains the drafts on it) or a draft on a model with no changes of its own.
                            items:
                              oneOf:
                                - $ref: '#/components/schemas/BranchModelPart'
                                - $ref: '#/components/schemas/BranchPartDraft'
                              discriminator:
                                propertyName: kind
                  success:
                    type: boolean
              example:
                branch:
                  id: "123e4567-e89b-12d3-a456-426614174001"
                  name: "feature/new-metrics"
                  description: null
                  openToViewers: false
                  createdAt: "2026-01-15T10:00:00.000Z"
                  updatedAt: "2026-01-20T14:30:00.000Z"
                  parts:
                    - kind: model
                      id: "223e4567-e89b-12d3-a456-426614174002"
                      modelId: "323e4567-e89b-12d3-a456-426614174003"
                      modelName: "ecommerce"
                      pullRequestUrl: null
                      allowed: true
                      drafts: []
                    - kind: draft
                      id: "423e4567-e89b-12d3-a456-426614174004"
                      documentId: "523e4567-e89b-12d3-a456-426614174005"
                      documentIdentifier: "abc123"
                      modelId: "323e4567-e89b-12d3-a456-426614174003"
                      name: "Revenue dashboard"
                      allowed: false
                      reason: no_content_edit_rights
                success: true
        '400':
          description: An Organization API key was used without a `userId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Branch is not visible to the user` - the branch exists but the caller can't discover it
            - A Personal Access Token was used with a `userId` naming a different member
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Branch with id <branchId> does not exist` - the ID names a retired branch, a model-level layer ID rather than an org-wide branch ID, or a branch in another organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
    patch:
      tags:
        - Branches
      summary: Open or close a branch to viewers
      description: |
        <Note>
          Opening a branch with this endpoint requires permission to edit every model in the branch.
          
          Closing a branch only requires permission to edit the branch on one of its member models.
        </Note>

        Open a branch to Viewers and Restricted Queriers - including embed users - so they see the branch's content instead of production, or close it again. See [Opening a branch to viewers](/content/develop/branch-mode#opening-a-branch-to-viewers) for more information.
      security:
        - bearerAuth: []
      operationId: setBranchOpenToViewers
      parameters:
        - $ref: '#/components/parameters/branchIdPath'
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - openToViewers
              properties:
                openToViewers:
                  type: boolean
                  description: |
                    `true` to open the branch to Viewers and Restricted Queriers, `false` to close it.
            example:
              openToViewers: true
      responses:
        '200':
          description: The branch was opened or closed successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  branch:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        description: The organization-wide branch ID.
                      name:
                        type: string
                        description: The branch's name.
                      openToViewers:
                        type: boolean
                        description: The branch's new state.
                  success:
                    type: boolean
                    description: Whether the operation succeeded. 
              example:
                branch:
                  id: "123e4567-e89b-12d3-a456-426614174001"
                  name: "feature/new-metrics"
                  openToViewers: true
                success: true
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `openToViewers` is missing or isn't a JSON boolean
            - An Organization API key was used without a `userId`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Opening a branch to viewers needs permission to edit every model in it`
            - Closing a branch to viewers needs permission to edit the branch on at least one of its models
            - A Personal Access Token was used with a `userId` naming a different member
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Branch with id <branchId> does not exist` - the ID names a retired branch, a model-level layer ID rather than an org-wide branch ID, or a branch in another organization
            - `User with id <userId> does not exist` - an Organization API key named a `userId` who hasn't accepted their invitation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/ApiError405'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/models:
    post:
      tags:
        - Models
      summary: Create model
      description: |
        Create a new model. The typical workflow for using this endpoint is:

        1. Create a schema model using this endpoint
        2. Use the [Refresh schema](/api/models/refresh-schema) endpoint to load the schema for the created model
        3. Create shared models based on the schema model

        You can also use this endpoint to create [branches](/content/develop/branch-mode).
      security:
        - bearerAuth: []
      operationId: createModel
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - connectionId
              properties:
                connectionId:
                  type: string
                  format: uuid
                  description: ID of the connection the model is based on.
                modelKind:
                  type: string
                  enum: [SCHEMA, SHARED, SHARED_EXTENSION, BRANCH]
                  default: SCHEMA
                  description: |
                    Type of model.

                    - `SCHEMA` - Mirrors the database structure
                    - `SHARED` - Represents the universal, governed data model that workbooks are based on
                    - `SHARED_EXTENSION` - Represents an extension to the `SHARED` model. Can be used to dynamically extend the `SHARED` model based on user attributes, or as a model that workbooks are based on in a departmental use case.
                    - `BRANCH` - A model layer that is used to develop modifications to the production shared model

                    For more information, see the [Modeling documentation](/modeling).
                modelName:
                  type: string
                  description: Name of the model.
                baseModelId:
                  type: string
                  description: |
                    **Applicable to branch and extension models.** ID of the base model.
                accessGrants:
                  type: array
                  default: []
                  description: List of [access grants](/modeling/develop/data-access-control) for the model.
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                        description: Name of the access grant.
                      accessBoostable:
                        type: boolean
                        description: If `true`, the access grant can be boosted.
                allowAsWorkbookBase:
                  type: boolean
                  default: false
                  description: |
                    **Only applicable to `SHARED_EXTENSION` models**. Controls whether users can explore and query the extension model. If `true`, the model appears in the model selector when creating a new analysis, and users and AI can run queries against it. If `false` (the default), the extension model is hidden from the model selector and all exploration, query access, and AI chat access are disabled. Set to `true` for any extension model intended for end-user or AI interaction.
                useIsolatedBranches:
                  type: boolean
                  default: false
                  description: |
                    **Only applicable to `SHARED_EXTENSION` models**. If `true`, the extension uses the **Develop independently** branch workflow: its branches hold a layer on the extension itself instead of on the parent shared model.
            examples:
              branchModel:
                summary: 'Create branch model'
                value:
                  connectionId: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
                  modelKind: 'BRANCH'
                  baseModelId: '5da4e30e-45e2-4693-b87d-51c39d69963f'
              sharedExtensionModel:
                summary: 'Create shared extension model'
                value:
                  connectionId: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
                  modelKind: 'SHARED_EXTENSION'
                  baseModelId: '5da4e30e-45e2-4693-b87d-51c39d69963f'
                  allowAsWorkbookBase: true
                  useIsolatedBranches: true
      responses:
        '200':
          description: Model created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: Error message if creation failed
                  message:
                    type: string
                    description: Additional message
                  model:
                    type: object
                    description: Details about the model
                    properties:
                      id:
                        type: string
                        format: uuid
                        description: ID of the created model.
                      modelKind:
                        type: string
                        description: The type of model.
                      name:
                        type: string
                        description: The name of the model.
                      orgBranchId:
                        type: string
                        format: uuid
                        description: |
                          ID of the branch as a whole. Set when `modelKind` is `BRANCH`; `null` otherwise. Use this ID with the [Branches API](/api/branches/list-branches).
                  success:
                    type: boolean
                    description: Indicates if the operation succeeded
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: Invalid parameter value`
            - `Bad Request: Schema model already exists for the connection`
            - `Bad Request: Schema model does not exist when creating a non-schema model`
            - `Bad Request: Shared model cannot be created when branch schema refresh is enabled`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Forbidden`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - Models
      summary: List models
      description: |
        Retrieves a paginated list of models with their metadata.

        An Organization API key can pass `userId` to list models as that member sees them, instead of every model in the organization. A Personal Access Token can only ever act as the calling user.

        Branches included via `include=activeBranches` are not filtered to the named member's access.
      security:
        - bearerAuth: []
      operationId: listModels
      parameters:
        - name: baseModelId
          in: query
          required: false
          schema:
            type: string
          description: |
            Filter models by base model ID.
        - name: connectionId
          in: query
          required: false
          schema:
            type: string
          description: |
            Filter models by connection ID.
        - name: includeDeleted
          in: query
          required: false
          schema:
            type: boolean
          description: |
            If `true`, include deleted models.
        - name: modelId
          in: query
          required: false
          schema:
            type: string
          description: |
            Filter models by a specific model ID.
        - name: modelKind
          in: query
          required: false
          schema:
            type: string
            enum: [SCHEMA, SHARED, SHARED_EXTENSION, WORKBOOK, BRANCH, QUERY, TOPIC, FIELD_PICKER_TOPIC]
          description: |
            Filter by model kind.
        - name: name
          in: query
          required: false
          schema:
            type: string
          description: |
            Filter models by name.
        - $ref: '#/components/parameters/pageSize'
        - $ref: '#/components/parameters/pageSize'
        - name: explorable
          in: query
          required: false
          schema:
            type: boolean
          description: |
            When `true`, returns only shared models (`modelKind: SHARED`) and shared extension models (`modelKind: SHARED_EXTENSION`) that can be used as a workbook base (`allowAsWorkbookBase: true`).
        - $ref: '#/components/parameters/sortDirection'
        - name: sortField
          in: query
          required: false
          schema:
            type: string
            default: updatedAt
            enum: [name, modelKind, connectionId, baseModelId, createdAt, updatedAt]
          description: |
            Field to sort by. 
        - name: include
          in: query
          schema:
            type: string
            enum: [activeBranches]
          description: |
            Comma-separated list of additional fields to include in the response.

            - `activeBranches` - Include active branches for each model
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: Paginated model list
          content:
            application/json:
              schema:
                type: object
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    description: List of model records.
                    items:
                      allOf:
                        - $ref: '#/components/schemas/Model'
                        - type: object
                          properties:
                            branches:
                              type: array
                              description: List of branch models. Only included when `include=activeBranches`.
                              items:
                                $ref: '#/components/schemas/Model'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Invalid cursor value` - the cursor is invalid, or a cursor from a request without `userId` was reused on a request with `userId`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden. Also returned when a Personal Access Token passes a `userId` naming a different member.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:
            - `userId` does not name a member of the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}:
    patch:
      tags:
        - Models
      summary: Rename model
      description: |
        <Note>
          **Connection Admin** permissions are required to rename a shared model or shared extension model. **Modeler** permissions and higher are required to rename a branch model.
        </Note>

        <Warning>
          Workbook models and query models cannot be renamed via this endpoint.
        </Warning>

        Rename a model. This endpoint allows you to programmatically rename models without using the UI, which is particularly useful for bulk operations on shared extension models created via `POST /api/v1/models`.

        The new name must be unique among active models with the same `modelKind` and `baseModelId`.

        **Branch-specific constraints:**

        - Name must match git-compatible regex: `[\w\-/]+`
        - No git ref path conflicts (e.g., cannot rename to `team` if `team/project` exists)
        - Cannot rename branches with an open pull request (when `pullRequestUrl` is set) — renaming would desync Omni from the git remote
      security:
        - bearerAuth: []
      operationId: renameModel
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: ID of the model to rename.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  description: The new name for the model.
                  example: "customer_metrics_v2"
      responses:
        '200':
          description: Model renamed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Model'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: Cannot rename models of kind WORKBOOK`
            - `Bad Request: Cannot rename models of kind QUERY`
            - `Bad Request: Model name must be unique`
            - `Bad Request: Branch name must match git-compatible regex [\w\-/]+`
            - `Bad Request: Branch name would create git ref path conflict`
            - `Bad Request: Cannot rename branch with open pull request`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Models
      summary: Archive shared or extension model
      description: |
        <Note>
          **Connection Admin** permissions are required to use this endpoint.
        </Note>

        Archive a shared or shared extension model, including all content and child extension models built on the target. For shared models, and shared extension models in the **Develop independently** branch workflow that own a Git repository, the model's Git configuration is also removed.

        This endpoint performs a soft delete - the model is recoverable from the [trash](/content/archive-restore). Restoring the model does not automatically restore its content, which must be restored separately.

        Only `SHARED` and `SHARED_EXTENSION` models can be deleted with this endpoint. To delete a branch model, use the [Delete branch API](/api/models/delete-branch).
      security:
        - bearerAuth: []
      operationId: deleteModel
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: ID of the model to delete.
      responses:
        '200':
          description: Model moved to trash successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: Cannot delete models of this kind` — returned for model kinds other than `SHARED` and `SHARED_EXTENSION`. To delete a branch model, use the [Delete branch API](/api/models/delete-branch).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model not found`
            - `Model already deleted`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/ai-agent-actions:
    get:
      tags:
        - Models
      summary: Get model AI agent actions
      description: |
        Return AI agent actions configured for a model — a unified list of sample queries and skills suitable for surfacing as suggested prompts in AI interfaces.

        Sample queries come from both `model.sample_queries` and each topic's `sample_queries`. Skills come from `model.skills` and each topic's `skills`, deduped by ID with topic skills overriding model skills.

        When no actions are configured at the model or topic level, `records` returns an empty array.
      security:
        - bearerAuth: []
      operationId: getModelAiAgentActions
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the model.
      responses:
        '200':
          description: AI agent actions retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Indicates the request was successful
                  records:
                    type: array
                    description: List of AI agent actions (sample queries and skills) configured for the model.
                    items:
                      type: object
                      required:
                        - type
                        - id
                        - label
                        - description
                        - prompt
                      properties:
                        type:
                          type: string
                          enum:
                            - sample_query
                            - skill
                          description: The type of action.
                        id:
                          type: string
                          description: Unique identifier for the action.
                        label:
                          type: string
                          description: Display label for the action.
                        description:
                          type: string
                          description: Description of what the action does.
                        prompt:
                          type: string
                          description: The prompt text to submit to the [Create AI job endpoint](/api/ai/create-ai-job). For skills, this is pre-formatted with the skill envelope. 
              example:
                success: true
                records:
                  - type: sample_query
                    id: revenue_by_region
                    label: Revenue by region
                    description: Show total revenue grouped by region
                    prompt: Show me revenue by region
                  - type: skill
                    id: analyze_trends
                    label: Analyze trends
                    description: Analyze trends in the data
                    prompt: "Skill: analyze_trends\nAnalyze trends in revenue over time"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `modelId: Invalid UUID`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Permission denied`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/suggestions/generate:
    post:
      tags:
        - AI Model Suggestions
      summary: Generate model suggestions
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Trigger an AI suggestion generation run for the shared model and enqueue the async job. Poll the [Get status for a generation run endpoint](/api/ai-model-suggestions/get-status-for-a-generation-run) for status.

        A model can have one active run at a time and a cooldown period is enforced after a successful run. See the `Retry-After` header in the response for more information.
      security:
        - bearerAuth: []
      operationId: modelSuggestionsGenerate
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: UUID of the shared model the suggestions belong to
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
      responses:
        '202':
          description: Generation run enqueued
          content:
            application/json:
              schema:
                type: object
                required:
                  - runId
                  - status
                properties:
                  runId:
                    type: string
                    format: uuid
                    description: |
                      The ID of the created generation run. Poll the [Get status for a generation run endpoint](/api/ai-model-suggestions/get-status-for-a-generation-run) for status.
                  status:
                    type: string
                    enum:
                      - queued
                    description: The status of the run. Generation runs asynchronously.
              example:
                runId: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                status: queued
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `modelId: Invalid UUID` - The model ID is not a valid UUID format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Permission denied` - User is not an Organization Admin
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Model not found in this organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed
        '409':
          description: A generation run is already active for this model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
              example:
                detail: "A generation run is already active for this model"
                status: 409
        '429':
          description: A run completed recently; retry after the cooldown (see the `Retry-After` header)
          headers:
            Retry-After:
              schema:
                type: integer
              description: Number of seconds to wait before retrying
          content:
            application/json:
              schema:
                type: object
                required:
                  - runId
                  - status
                properties:
                  runId:
                    type: string
                    format: uuid
                    description: |
                      The ID of the created generation run. Poll the [Get status for a generation run endpoint](/api/ai-model-suggestions/get-status-for-a-generation-run) for status.
                  status:
                    type: string
                    enum:
                      - queued
                    description: The status of the run. Generation runs asynchronously.
              example:
                detail: "A run completed recently. Please wait before triggering another run."
                lastCompletedAt: "2026-07-20T10:30:00.000Z"
                retryAfterSeconds: 300
                status: 429
        '500':
          description: Failed to enqueue the generation job

  /v1/models/{modelId}/suggestions/schedule:
    put:
      tags:
        - AI Model Suggestions
      summary: Enable model AI suggestions schedule
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Enable the daily generation schedule for AI model suggestions on a shared model. Suggestions are generated daily at midnight in the specified timezone.

        This operation is idempotent: calling it multiple times returns the persisted schedule.
      security:
        - bearerAuth: []
      operationId: modelSuggestionsScheduleEnable
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the model.
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                timezone:
                  type: string
                  description: |
                    IANA timezone string (e.g., `America/New_York`, `Europe/London`, `UTC`). Determines when the daily suggestion generation runs. Defaults to `UTC` if not provided.
              additionalProperties: false
            examples:
              withTimezone:
                summary: Enable with specific timezone
                value:
                  timezone: "America/New_York"
              defaultTimezone:
                summary: Enable with default UTC timezone
                value: {}
      responses:
        '200':
          description: Schedule enabled successfully. Returns the current state of the schedule, or the same state if already enabled.
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                  - sharedModelId
                  - status
                  - timezone
                properties:
                  id:
                    type: string
                    format: uuid
                    description: Unique identifier for the schedule.
                  sharedModelId:
                    type: string
                    format: uuid
                    description: The shared model the schedule generates suggestions for.
                  status:
                    type: string
                    enum:
                      - enabled
                  timezone:
                    type: string
                    description: IANA timezone the schedule runs in.
                    example: America/New_York
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                sharedModelId: f1e2d3c4-b5a6-7890-abcd-ef9876543210
                status: "enabled"
                timezone: "America/New_York"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `modelId: Invalid UUID` - The model ID is not a valid UUID format
            - `timezone: Invalid timezone` - The provided timezone is not a valid IANA timezone format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Permission denied` - User is not an organization admin
            - `AI is not enabled for this organization` - Organization does not have AI features enabled
            - `Model must be shared` - The model is not a shared model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - AI Model Suggestions
      summary: Disable model AI suggestions schedule
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Disable the daily generation schedule for AI model suggestions on a shared model.

        This operation is idempotent: calling it multiple times returns success even if the schedule is already disabled.
      security:
        - bearerAuth: []
      operationId: modelSuggestionsScheduleDisable
      parameters:
        - schema:
            type: string
            format: uuid
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
          required: true
          description: UUID of the shared model the suggestions belong to
          name: modelId
          in: path
      responses:
        '200':
          description: Schedule disabled successfully. Returns confirmation even if the schedule was already disabled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
              example:
                success: true
                message: "AI suggestions schedule disabled"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `modelId: Invalid UUID` - The model ID is not a valid UUID format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Permission denied` - User is not an Organization Admin
            - `AI is not enabled for this organization` - Organization does not have AI features enabled
            - `Model must be shared` - The model is not a shared model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/branch/{branchName}:
    delete:
      tags:
        - Models
      summary: Delete branch
      description: Deletes a branch associated with the specified shared model.
      security:
        - bearerAuth: []
      operationId: deleteModelBranch
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: ID of the shared model.
        - name: branchName
          in: path
          required: true
          schema:
            type: string
          description: Name of the branch to delete.
      responses:
        '200':
          description: Branch deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: Invalid modelId format`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Shared model or branch model does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/refresh:
    post:
      tags:
        - Models
      summary: Refresh schema
      description: |
        <Note>
          This endpoint requires either **Connection Admin** or **Modeler** permissions:

          - **Modelers** can use this endpoint on connections that have exactly **one** shared model
          - **Connection Admins** can use this endpoint on any connection they are an admin of, whether the connection has one or multiple models
        </Note>

        Refresh the schema of the specified model. This will cause the model to reflect the latest changes to schemas, views, and fields from the data source. Schema refreshes will remove structures that are no longer present in the source, but not anything created by users.

        Depending on whether the **Branch-based schema refresh** setting is configured:

        - If **Branch-based schema refresh** is enabled, the `branch_id` query parameter is required. The `branch_id` is validated against the shared model.
        - If **Branch-based schema refresh** isn't enabled, do not provide the `branch_id` parameter. The API will return a `400` error in this case.
      security:
        - bearerAuth: []
      operationId: refreshSchema
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the model to be refreshed.
        - name: branch_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: |
            **Required if Branch-based schema refresh is enabled**. The ID of the branch for models with this setting enabled.

            Do not provide this parameter when the setting is not enabled.
        - name: hard_refresh
          in: query
          required: false
          schema:
            type: boolean
            default: true
          description: |
            Whether to perform a hard refresh (removes dropped objects) or soft refresh (additive only). Defaults to true (hard refresh).

            When combined with `schemas` or `tables`, a hard refresh is scoped to the selection: dropped objects are removed only inside the selected schemas or tables, and the rest of the model is left untouched.
        - name: schemas
          in: query
          required: false
          schema:
            type: string
          description: |
            Comma-separated list of schemas to selectively refresh. Only the listed schemas are reloaded; the rest of the model is preserved.

            Unless `hard_refresh=false` is also set, objects inside the selected schemas that are no longer present in the data source are removed.
        - name: tables
          in: query
          required: false
          schema:
            type: string
          description: |
            Comma-separated list of tables to selectively refresh. Only the listed tables are reloaded; the rest of the model is preserved.

            Unless `hard_refresh=false` is also set, objects inside the selected tables that are no longer present in the data source are removed.
      responses:
        '200':
          description: Model refresh started
          content:
            application/json:
              schema:
                type: object
                properties:
                  jobId:
                    type: string
                    format: uuid
                    description: ID of the job.
                  modelId:
                    type: string
                    format: uuid
                    description: ID of the model.
                  status:
                    type: string
                    example: "running"
                    description: Status of the schema refresh. This value will be `running` to indicate that the refresh has started.
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: modelId: Invalid uuid`
            - `Bad Request: branch_id: Invalid uuid`
            - `Bad Request: branch_id is required when branch schema refresh is enabled`
            - `Bad Request: branch_id must not be provided when branch schema refresh is not enabled`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: |
            Not Found

            Possible causes:

            - Model not found
            - Branch not found or does not belong to the model's shared model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/cache_reset/{cachePolicyName}:
    post:
      tags:
        - Models
      summary: Reset cache
      description: |
        Reset the cache for the specified cache policy.

        <Note>
          `cachePolicyName` values are not validated against existing policies. Verify that the values you provide are exact matches to policies in the model.
        </Note>
      security:
        - bearerAuth: []
      operationId: resetModelCache
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the model associated with the cache policy.
        - name: cachePolicyName
          in: path
          required: true
          schema:
            type: string
          description: The name of the cache policy to reset.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                resetAt:
                  type: string
                  format: date-time
                  description: |
                    An ISO-8601 date string that sets the time the cache should be reset (invalidated). When set, cache entries created between this value and the current time will still be considered valid. Cannot be a future date. Defaults to now.
      responses:
        '200':
          description: Cache reset successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  cache_reset:
                    type: object
                    description: The cache reset details.
                    properties:
                      id:
                        type: string
                        format: uuid
                        description: ID of the cache reset record.
                      model_id:
                        type: string
                        format: uuid
                        description: ID of the model.
                      policy_name:
                        type: string
                        description: Name of the cache policy.
                      created_at:
                        type: string
                        format: date-time
                        description: Timestamp when the cache reset record was created.
                      updated_at:
                        type: string
                        format: date-time
                        description: Timestamp when the cache reset record was last updated.
                      reset_at:
                        type: string
                        format: date-time
                        description: Timestamp when the cache was reset.
                  success:
                    type: boolean
                    description: Indicates the request was successful.
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Model with id <modelId> does not exist`
            - `resetAt cannot be future dated`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/reparse:
    post:
      tags:
        - Models
      summary: Re-parse model SQL
      description: |
        <Note>
          **Querier** permissions or higher on a model are required to use this endpoint.
        </Note>

        Re-parse all SQL fields and views in a shared model or shared extension and refresh their inferred column types. Use this to clear a SQL validation error after resolving the issue - such as a missing database grant - without editing the SQL itself.
        
        Re-parsing the shared model itself, or a branch on a Git follower model, only saves if the result leaves the model's YAML unchanged; otherwise the request fails and nothing is saved. Re-parsing an ordinary branch saves YAML changes normally, such as newly inferred dimensions.

        This request runs synchronously and can take several minutes on a large model. If your client times out before it finishes, the re-parse still completes and saves on the server.  
      security:
        - bearerAuth: []
      operationId: modelsReparse
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            description: Model UUID
            example: 123e4567-e89b-12d3-a456-426614174000
          description: UUID of the shared model or shared extension to re-parse.
        - name: branch_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
            example: 123e4567-e89b-12d3-a456-426614174001
          description: |
            ID of a branch model to re-parse. This can be a branch of the shared model, or for a shared extension, a branch of its shared model. If the extension uses the [**Develop independently** branch workflow](/modeling/develop/shared-extensions/extension-branches), pass the ID of the extension itself.
            
            Unlike re-parsing an entire model, re-parsing a branch saves even if the branch model's YAML changes. The exception is when this occurs on a Git follower model.
      responses:
        '200':
          description: Model re-parsed and saved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
              example:
                success: true
        '400':
          description: |
            Bad Request

            Possible causes:

            - Invalid `modelId`, `branch_id`, or an unknown query parameter
            - The re-parse would change the YAML of the model itself, or of a branch on a Git follower model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Could not re-parse model: Forced type revalidation would change the model's YAML"
                status: 400
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Forbidden

            Possible causes:

            - The caller doesn't have permission to edit the model, or its branch if `branch_id` is provided
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible causes:

            - Model not found, or deleted
            - `branch_id` doesn't belong to the model's branch parent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/ApiError405'
        '409':
          description: The model or branch was edited during the re-parse. Retry the request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
        '422':
          description: The model is not a shared model or shared extension, for example a workbook model.
          content:
            application/json:
              schema:
                $ref: '#/components/responses/ApiError422'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/yaml:
    post:
      tags:
        - Models
      summary: Create or update YAML files
      description: |
        Create or overwrite a YAML file for a model. The file can be a special file (`model` or `relationships`) or a YAML file ending in `.topic`, `.view`, or `.composite_topic`.

        The following models cannot be edited using this endpoint:

        - Schema models
        - Models using git follower mode
      security:
        - bearerAuth: []
      operationId: updateModelYaml
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the model.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - fileName
                - yaml
                - mode
              properties:
                branchId:
                  type: string
                  format: uuid
                  description: |
                    **Required if git pull requests are required for the model.** The ID of the branch to create or update. To retrieve branch IDs, use the [List models](/api/models/list-models) endpoint with `modelKind=BRANCH`.
                fileName:
                  type: string
                  description: |
                    The name of the YAML file. Valid values are:

                    - `model` - [Model file](/modeling/models)
                    - `relationships` - [Relationships file](/modeling/relationships)
                    - `<topic_name>.topic` - [Topic file](/modeling/topics/parameters)
                    - `<composite_topic_name>.composite_topic` - [Composite topic file](/modeling/composite-topics/parameters)
                    - `<view_name>.view` - [View file](/modeling/views)
                yaml:
                  type: string
                  description: |
                    The YAML contents of the file. This can be empty, in which case:

                    - If empty and used with `mode: extension` the file will be removed from the model
                    - If empty and used with `mode: combined`, the file will be ignored in the model
                mode:
                  type: string
                  default: combined
                  enum: [combined, extension, staged, merged, history]
                  description: |
                    The mode to use when creating or overwriting the YAML file:

                    - `combined` - Workbook models must use this mode if there is a `branchId`.
                    - `extension`
                    - `staged`
                    - `merged` - Requires a `branchId` on a shared or shared extension model.
                    - `history`
                commitMessage:
                  type: string
                  description: |
                    **Required for [git-enabled models](/integrations/git).** Commit message describing the change.
                previousChecksum:
                  type: string
                  description: |
                    Checksum of the file when fetched, for conflict detection. Obtain this value from the [Get model YAML endpoint](/api/models/get-model-yaml) by including the `includeChecksums` parameter.

                    If the file has been modified since you fetched it, the request will fail with a `File has been modified since it was fetched` error.
                rejectIfViewExists:
                  type: boolean
                  description: |
                    **Only applies to `<view_name>.view` files**. If `true`, rejects the upload when the target model layer already defines the resolved view name. Doesn't apply to uploads that extend an inherited view.

                    Incompatible with `branchId`, as a branch only sees the branch's own views and can't reliably identify a duplicate in the shared model.

                    Omitted or `false` preserves the existing behavior, where the upload merges into or duplicates an existing view.
                fullyResolved:
                  type: boolean
                  default: false
                  description: |
                    If `true`, accepts fully-resolved YAML. This means that Omni will resolve any extension (`extends`) usage and place the changes into the appropriate model files.

                    When `false` (default), the YAML is saved as-is to the specified file. This means that even if extensions are used, they will not be included in the changes.
            examples:
              rejectIfViewExists:
                summary: Reject the upload if the view already exists
                value:
                  fileName: orders.view
                  mode: combined
                  yaml: "dimensions:\n  id:\n    sql: ${TABLE}.id\n"
                  rejectIfViewExists: true
      responses:
        '200':
          description: YAML file updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  fileName:
                    type: string
                    description: The name of the file that was created or updated.
                  success:
                    type: boolean
                    description: Indicates the request was successful.
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `<parameter>: <parameter> is required`
            - `modelId: Invalid uuid`
            - `branchId: Invalid uuid`
            - `<parameter>: Invalid value <description>`
            - `File has been modified since it was fetched`
            - `View "<name>" already exists.`
            - `rejectIfViewExists=true cannot be used with branchId`
            - `Mode 'merged' requires a branchId on a shared or shared extension model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                duplicateView:
                  summary: Rejected because the view already exists
                  value:
                    detail: 'View "orders" already exists.'
                    status: 400
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Permission denied`
            - `Feature not enabled`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
            - `Branch does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - Models
      summary: Get model YAML
      description: Retrieve the YAML representation of a model with optional filtering and mode selection.
      security:
        - bearerAuth: []
      operationId: getModelYaml
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the model.
        - name: branchId
          in: query
          schema:
            type: string
            format: uuid
          description: The ID of the branch to retrieve YAML from. Only valid for shared models in `combined` mode. To retrieve branch IDs, use the [List models](/api/models/list-models) endpoint with `modelKind=BRANCH`.
        - name: fileName
          in: query
          schema:
            type: string
          description: Filter YAML files by name. Can be a string or a regex pattern.
        - name: mode
          in: query
          schema:
            type: string
            default: combined
            enum: [extension, staged, combined]
          description: |
            The mode to use when retrieving YAML.

            - `extension` - Returns extension-only format
            - `staged` - Returns staged changes
            - `combined` - Returns combined YAML
        - name: includeChecksums
          in: query
          schema:
            type: boolean
            default: false
          description: |
            If `true`, the response will include file checksums for concurrency control.

            A file's checksum can be passed as a query parameter in the [Create or update YAML endpoint](/api/models/create-or-update-yaml-files) to ensure changes made by another user aren't overwritten.
        - name: includeSchemas
          in: query
          schema:
            type: string
          description: |
            Filters the returned YAML to only include views from the specified schema. Relationships are preserved even if they reference views outside the specified schema.

            This parameter accepts a single schema name only. Use the [List model schemas endpoint](/api/models/list-model-schemas) to discover available schema names.
        - name: fullyResolved
          in: query
          schema:
            type: boolean
            default: false
          description: |
            If `true`, returns YAML with any extensions (`extends`) resolved. This will match what runs at query time.

            When `false` (default), returns the YAML as authored in the model files. This means even if extensions are used, they will not be included in the returned YAML.
      responses:
        '200':
          description: Model YAML specification
          content:
            application/json:
              schema:
                type: object
                properties:
                  files:
                    type: object
                    additionalProperties:
                      type: string
                    description: Map of file paths to YAML content.
                  version:
                    type: integer
                    description: Version of the YAML.
                  viewNames:
                    type: object
                    description: Map of view names to their definitions.
                  checksums:
                    type: object
                    description: Map of file paths to their checksums.
              example:
                files:
                  model.yaml: "name: Test Model\ntype: model"
                  views/customers.yaml: "name: Customers\nfields: []"
                  views/orders.yaml: "name: Orders\nfields: []"
                version: 1
                viewNames: {}
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Schema models do not have branches`
            - `Branches are not valid for workbook models with mode=extension, staged`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Permission denied`
            - `Feature not enabled`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
            - `Branch does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          description: |
            Internal Server Error

            Possible error messages:

            - `Failed to get model YAML`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Models
      summary: Delete a YAML file
      description: |
        Delete a topic, composite topic, or view YAML file from a model. The file must be a YAML file ending in `.topic`, `.composite_topic`, or `.view`.

        The following models cannot be edited using this endpoint:

        - Schema models
        - Models using git follower mode
      security:
        - bearerAuth: []
      operationId: deleteModelYaml
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the model.
        - name: fileName
          in: query
          required: true
          schema:
            type: string
          description: |
            The name of the YAML file to delete. Valid values are:

            - `<topic_name>.topic` - [Topic file](/modeling/topics/parameters)
            - `<composite_topic_name>.composite_topic` - [Composite topic file](/modeling/composite-topics/parameters)
            - `<view_name>.view` - [View file](/modeling/views)
        - name: branchId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            **Required if git pull requests are required for the model.** The ID of the branch to delete from. To retrieve branch IDs, use the [List models](/api/models/list-models) endpoint with `modelKind=BRANCH`.
        - name: mode
          in: query
          schema:
            type: string
            default: combined
            enum: [combined, extension, staged, merged, history]
          description: |
            The mode to use when deleting the YAML file.

            - `combined` - Workbook models must use this mode if there is a `branchId`.
            - `extension`
            - `staged`
            - `merged` - Requires a `branchId` on a shared or shared extension model.
            - `history`
        - name: commitMessage
          in: query
          schema:
            type: string
          description: |
            **Required for [git-enabled models](/integrations/git).** Commit message describing the change.
      responses:
        '200':
          description: YAML file deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  fileName:
                    type: string
                    description: The name of the file that was deleted.
                  success:
                    type: boolean
                    description: Indicates the request was successful.
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `<parameter>: <parameter> is required`
            - `modelId: Invalid uuid`
            - `branchId: Invalid uuid`
            - `<parameter>: Invalid value <description>`
            - `Mode 'merged' requires a branchId on a shared or shared extension model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Permission denied`
            - `Feature not enabled`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
            - `Branch does not exist`
            - `File does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/schemas:
    get:
      tags:
        - Models
      summary: List model schemas
      description: Retrieves a sorted list of all available schema names for the specified model, including physical, virtual, and dynamic schemas.
      security:
        - bearerAuth: []
      operationId: getModelSchemas
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the model.
        - name: branchId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            The ID of the branch to retrieve schemas from.
      responses:
        '200':
          description: List of schema names
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
                description: Sorted array of schema names available in the model.
              example:
                - ANALYTICS
                - PUBLIC
                - STAGING
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Permission denied`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/dbt-exposures:
    get:
      operationId: getDbtExposures
      summary: Get dbt exposures
      description: |
        <Note>
          **Connection Admin** permissions are required to use this endpoint.
        </Note>

        Retrieve dbt exposures for the specified model. This endpoint computes exposures on-demand by analyzing which dbt models are referenced by dashboards and apps that use the specified model.

        This enables automated retrieval of exposure data previously only available via the manual **Sync exposures** flow in Omni, making it ideal for CI/CD pipelines and automated workflows.
      tags:
        - dbt
      security:
        - bearerAuth: []
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: ID of the model
          example: "550e8400-e29b-41d4-a716-446655440000"
        - name: pageSize
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Number of results per page (1-100).
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Pagination cursor from a previous response's `pageInfo.nextCursor`.
        - name: branch_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Branch ID for branch-aware model operations.
      responses:
        '200':
          description: dbt exposures successfully retrieved
          content:
            application/json:
              schema:
                type: object
                required:
                  - pageInfo
                  - records
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    items:
                      type: object
                      required:
                        - dashboard_identifier
                        - deduplication_name
                        - exposure
                      properties:
                        dashboard_identifier:
                          type: string
                          description: Unique identifier of the dashboard or app associated with this exposure.
                          example: "abc123"
                        deduplication_name:
                          type: string
                          description: Unique name used for deduplication, combining the exposure name and dashboard identifier.
                          example: "sales_overview_abc123"
                        exposure:
                          type: object
                          description: |
                            The generated exposure, with a structure matching the format defined in [dbt's exposures documentation](https://docs.getdbt.com/docs/build/exposures).
                            
                            This field will be `null` if the dashboard doesn't reference any dbt models.
                          required:
                            - name
                            - type
                            - owner
                            - depends_on
                            - label
                            - url
                          properties:
                            name:
                              type: string
                              description: Name of the exposure. Generated from dashboard title.
                              example: "sales_overview"
                            type:
                              type: string
                              description: Type of the exposure. Either `dashboard` or `application`.
                              example: "dashboard"
                            owner:
                              type: object
                              required:
                                - name
                                - email
                              properties:
                                name:
                                  type: string
                                  description: Owner's name
                                  example: "Blob Ross"
                                email:
                                  type: string
                                  description: Owner's email address
                                  example: "blob.ross@blobsrus.com"
                            depends_on:
                              type: array
                              items:
                                type: string
                              description: Array of dbt ref strings for models this exposure depends on
                              example: ["ref('orders')", "ref('customers')"]
                            label:
                              type: string
                              description: Human-readable label for the exposure
                              example: "Sales Overview"
                            url:
                              type: string
                              description: URL to the dashboard or app
                              example: "https://your-org.omni.co/dashboards/abc123"
              example:
                pageInfo:
                  hasNextPage: false
                  nextCursor: null
                  pageSize: 1000
                  totalRecords: 2
                records:
                  - dashboard_identifier: "abc123"
                    deduplication_name: "sales_overview_abc123"
                    exposure:
                      name: "sales_overview"
                      type: "dashboard"
                      owner:
                        name: "Blob Ross"
                        email: "blob.ross@blobsrus.com"
                      depends_on: ["ref('orders')", "ref('customers')"]
                      label: "Sales Overview"
                      url: "https://your-org.omni.co/dashboards/abc123"
                  - dashboard_identifier: "def456"
                    deduplication_name: "sales_app_def456"
                    exposure:
                      name: "sales_app"
                      type: "application"
                      owner:
                        name: "Blob Ross"
                        email: "blob.ross@blobsrus.com"
                      depends_on: ["ref('orders')", "ref('customers')"]
                      label: "Sales App"
                      url: "https://your-org.omni.co/w/def456"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: Invalid modelId format`
            - `cursor: Invalid UUID`
            - `pageSize: Page size must be at least 1`
            - `pageSize: Page size cannot exceed 100`
            - `Bad Request: branch_id: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/branch/{branchName}/dbt:
    post:
      tags:
        - dbt
      summary: Set dbt environment on model branch
      description: |
        Sets the active dbt environment on a model branch. This endpoint allows you to programmatically configure which dbt environment a branch should use, enabling CI/CD pipelines to automate dbt environment configuration before triggering schema refreshes.
      security:
        - bearerAuth: []
      operationId: setModelBranchDbtEnvironment
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the model
          example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        - name: branchName
          in: path
          required: true
          schema:
            type: string
          description: The name of the branch to configure
          example: "feature/new-models"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - dbt_environment_id
              properties:
                dbt_environment_id:
                  type: string
                  format: uuid
                  description: The ID of the dbt environment to set as active for the branch
                  example: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                dbt_git_branch:
                  type: string
                  description: Optional git branch name to associate with the dbt environment
                  example: "feature/branch"
            example:
              dbt_environment_id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
              dbt_git_branch: "feature/branch"
      responses:
        '200':
          description: dbt environment configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: Invalid modelId format`
            - `Bad Request: Invalid dbt_environment_id format`
            - `dbt_environment_id is required`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Shared model or branch model does not exist`
            - `Branch model with id <branch-id> does not exist`
            - `dbt environment not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/branch/{branchName}/merge:
    post:
      tags:
        - Model branches
      summary: Merge a branch
      description: |
        Merge a model branch into the shared model.
      x-mint:
        content: |
          For PR-required and [git follower](/integrations/git/follower-mode) models, direct merges via API are rejected by default as they would bypass the intended git workflow. You can use the `force_override_git_settings` parameter to override this check when necessary, but git will not be synced to avoid force-pushing to `main`.

          <Warning>
            The `force_override_git_settings` parameter requires **Connection Admin** or **Organization Admin** permissions. Users with lesser permissions will receive a `403 Forbidden` error when attempting to use this parameter.
          </Warning>

          | Model Configuration | Default Behavior | With `force_override_git_settings: true` |
          |---------------------|------------------|------------------------------------------|
          | No git | Merge succeeds, no git sync | N/A |
          | Git enabled (no PR required) | Merge succeeds, syncs to git | N/A |
          | Git + PR required | Rejected with 400 error | Merge succeeds, no git sync |
          | Git + git follower | Rejected with 400 error | Merge succeeds, no git sync |

          Merging a branch on a shared extension model in the **Develop independently** branch workflow syncs the extension's own Git repository, not its parent model's.
      security:
        - bearerAuth: []
      operationId: mergeBranch
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the model
          example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        - name: branchName
          in: path
          required: true
          schema:
            type: string
          description: The name of the branch to merge
          example: "feature/add-revenue-metrics"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                delete_branch:
                  type: boolean
                  default: false
                  description: Delete the branch after merging
                publish_drafts:
                  type: boolean
                  default: true
                  description: When enabled, publish branch-attached drafts when merging
                commit_message:
                  type: string
                  description: Custom commit message for git sync. Defaults to `"branch <name> merged via API"`
                  example: "Merged revenue metrics branch via CI/CD pipeline"
                force_override_git_settings:
                  type: boolean
                  default: false
                  description: |
                    **Requires Connection Admin or Organization Admin permissions.** Users with lesser permissions will receive a `403 Forbidden` error when attempting to use this parameter.
                    
                    Allow merge for PR-required or git-follower models. When enabled, the merge will succeed but git will not be synced to avoid force-pushing to main. 
            example:
              delete_branch: true
              publish_drafts: true
              commit_message: "Merged revenue metrics branch via CI/CD pipeline"
              force_override_git_settings: false
      responses:
        '200':
          description: Branch merged successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Whether the merge was successful
                    example: true
                  published_drafts_count:
                    type: integer
                    description: Number of drafts that were published during the merge
                    example: 2
                  failed_drafts_count:
                    type: integer
                    description: Number of drafts that failed to publish
                    example: 0
                  git_synced:
                    type: boolean
                    description: Whether the changes were synced to git
                    example: true
              example:
                success: true
                published_drafts_count: 2
                failed_drafts_count: 0
                git_synced: true
        '400':
          description: Bad Request - Merge not allowed for this model configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                prRequired:
                  summary: PR-required model without override
                  value:
                    error: "Cannot merge branch directly. This model requires pull requests for changes. Use force_override_git_settings: true to bypass."
                gitFollower:
                  summary: Git-follower model without override
                  value:
                    error: "Cannot merge branch directly. This model follows git as source of truth. Use force_override_git_settings: true to bypass."
        '403':
          description: Forbidden - Insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                insufficientPermissions:
                  summary: Modeler attempting to use force_override_git_settings
                  value:
                    error: "Insufficient permissions. The force_override_git_settings flag requires Connection Admin or higher permissions."
        '404':
          $ref: '#/components/responses/NotFound'
        '405':
          description: Invalid HTTP method
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/models/{modelId}/validate:
    get:
      tags:
        - Models
      summary: Validate model
      description: Retrieve validation issues for a model and its branches.
      security:
        - bearerAuth: []
      operationId: validateModel
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the model.
        - name: branchId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            The ID of the branch to validate. The branch must exist for the model. If not provided, the model is validated directly. To retrieve branch IDs, use the [List models](/api/models/list-models) endpoint with `modelKind=BRANCH`.

            **Note**: Schema models do not support branches.
      responses:
        '200':
          description: A list of validation issues.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    message:
                      type: string
                      description: The validation issue message.
                    is_warning:
                      type: boolean
                      description: If `true`, the issue is a warning. If `false`, the issue is an error.
                    yaml_path:
                      type: string
                      description: Where the issue is, as a comma-separated path from the model file through the YAML keys down to the object, for example `orders.view,dimensions,total`.
                    slug:
                      type: string
                      description: Stable identifier for the kind of issue.
                    prop_name:
                      type: string
                      description: Model property the issue is about.
                    auto_fix:
                      type: object
                      description: Information about automatic fixes available for the issue.
                      properties:
                        description_short:
                          type: string
                          description: A short description of the auto-fix.
                        description_unique:
                          type: string
                          description: A unique description of the auto-fix.
                  required:
                    - message
                    - is_warning
                    - yaml_path
                    - slug
              example:
                - message: "No view \"blob_sales\". Set base_view to a valid, existing view."
                  is_warning: false
                  yaml_path: "blob_sales.topic"
                  slug: "missing-base-view"
                  auto_fix:
                    description_short: "Delete topic \"blob_sales\""
                    description_unique: "Delete topic \"blob_sales\""
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `modelId: Invalid uuid`
            - `Schema models do not have branches`
            - `Unrecognized key(s) in object: '<invalidParameter>'`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model with id <modelId> does not exist`
            - `Branch model with id <branchId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          description: |
            Internal Server Error

            Possible error messages:

            - `Model service error`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/models/{modelId}/git:
    get:
      tags:
        - Model Git configuration
      summary: Get Git configuration
      description: |
        Retrieve the Git configuration for a shared model, or a shared extension model in the **Develop independently** branch workflow that owns its own Git repository, separate from its parent model's.
      security:
        - bearerAuth: []
      operationId: getModelGitConfig
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the shared model, or a shared extension model in the **Develop independently** branch workflow.
        - name: include
          in: query
          required: false
          schema:
            type: string
            enum: [webhookSecret]
          description: |
            Comma-separated list of additional fields to include in the response.

            - `webhookSecret` - Include the webhook secret in the response
      responses:
        '200':
          description: Git configuration retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelGitConfig'
              example:
                authMethod: "ssh"
                baseBranch: "main"
                branchPerPullRequest: false
                gitFollower: false
                gitServiceProvider: "github"
                modelPath: "omni/blobs_r_us"
                publicKey: "ssh-ed25519 AAAA..."
                requirePullRequest: "users-only"
                cloneUrl: "git@github.com:org/repo.git"
                webUrl: "https://github.com/org/repo"
                webhookUrl: "https://app.omni.co/api/webhooks/model/..."
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: modelId: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Forbidden: Requires MANAGE_MODEL permission`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Not Found: Model does not exist`
            - `Not Found: Git configuration not found for this model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
        - Model Git configuration
      summary: Create Git configuration
      description: |
        Create a new Git configuration for a shared model, or a shared extension model in the **Develop independently** branch workflow. A shared extension's Git configuration is its own, separate from its parent model's.
      security:
        - bearerAuth: []
      operationId: createModelGitConfig
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the shared model, or a shared extension model in the **Develop independently** branch workflow.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - cloneUrl
              properties:
                authMethod:
                  type: string
                  enum: [ssh, https_token]
                  description: |
                    The authentication method to use:

                    - `ssh` for deploy key
                    - `https_token` for deploy token/PAT
                  example: "ssh"
                cloneUrl:
                  type: string
                  description: |
                    Clone URL of the git repository. Varies depending on the `authMethod`:

                    - `ssh` - A string starting with `git@...`
                    - `https_token` - A string starting with `https://...`
                  example: "git@github.com:org/repo.git"
                sshUrl:
                  type: string
                  deprecated: true
                  description: |
                    **Deprecated. Use `cloneUrl` instead**. The SSH URL of the git repository.
                  example: "git@github.com:org/repo.git"
                token:
                  type: string
                  maxLength: 1000
                  pattern: ^[a-zA-Z0-9_\\-.]+$
                  description: |
                    **Required when `authMethod: https_token`**. HTTPS token for authentication (deploy token value, PAT, etc.).
                baseBranch:
                  type: string
                  default: "main"
                  description: The target branch for Omni pull requests.
                  example: "main"
                branchPerPullRequest:
                  type: boolean
                  default: false
                  description: If `true`, all pull requests will create a branch in Omni, even those created outside of the tool.
                  example: false
                gitFollower:
                  type: boolean
                  default: false
                  description: If `true`, the shared model is read-only and can only be updated by merging pull requests to the `baseBranch`.
                  example: false
                gitServiceProvider:
                  type: string
                  enum: [auto, github, gitlab, azure_devops, bitbucket, bitbucket_datacenter]
                  default: "auto"
                  description: |
                    The git provider type. Use `auto` for automatic detection based on the SSH URL.

                    - `auto` - Automatically detect the provider
                    - `github` - GitHub
                    - `gitlab` - GitLab
                    - `azure_devops` - Azure DevOps
                    - `bitbucket` - Bitbucket Cloud
                    - `bitbucket_datacenter` - Bitbucket Data Center (self-hosted)
                  example: "github"
                modelPath:
                  type: string
                  description: Path to model files within the repository.
                  example: "omni/blobs_r_us"
                requirePullRequest:
                  type: string
                  enum: [always, users-only, never]
                  default: "never"
                  description: |
                    Controls when pull requests are required for changes:

                    - `always` - Required for all changes
                    - `users-only` - Required only for user-initiated changes
                    - `never` - Never required
                  example: "users-only"
                webUrl:
                  type: string
                  description: Custom web URL for the git repository. Use when the `cloneUrl` goes through a tunnel/VPC and differs from the inferred HTTPS address.
                  example: "https://github.com/org/repo"
                deployPrivateKey:
                  type: string
                  description: |
                    **Applicable to SSH authentication**. Supply your own RSA or ED25519 private key in PEM format (OpenSSH, PKCS#1, or PKCS#8) instead of using an Omni-generated keypair. Authorize the matching public key with your first, then set it here. See [Rotate Git SSH keys with the API](/guides/api/rotate-ssh-deploy-keys) for step-by-step instructions.
                  example: "-----BEGIN OPENSSH PRIVATE KEY-----\n..."
                deployKeyPassphrase:
                  type: string
                  description: |
                    **Applicable to SSH authentication.** Passphrase used to decrypt an encrypted `deployPrivateKey`. Omni uses it once to decrypt the key, then stores the key under its own encryption at rest. The passphrase itself is not retained. See [Rotate Git SSH keys with the API](/guides/api/rotate-ssh-deploy-keys) for step-by-step instructions.
                  example: "my-secure-passphrase"
            example:
              authMethod: "ssh"
              cloneUrl: "git@github.com:org/repo.git"
              baseBranch: "main"
              gitServiceProvider: "github"
              modelPath: "omni/blobs_r_us"
              requirePullRequest: "users-only"
      responses:
        '200':
          description: Git configuration created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelGitConfig'
              example:
                authMethod: "ssh"
                baseBranch: "main"
                branchPerPullRequest: false
                gitFollower: false
                gitServiceProvider: "github"
                modelPath: "omni/my_model"
                publicKey: "ssh-ed25519 AAAA..."
                requirePullRequest: "users-only"
                cloneUrl: "git@github.com:org/repo.git"
                webUrl: "https://github.com/org/repo"
                webhookSecret: "whsec_abc123xyz789"
                webhookUrl: "https://app.omni.co/api/webhooks/model/..."
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: modelId: Invalid uuid`
            - `Bad Request: sshUrl is required`
            - `Bad Request: Invalid sshUrl format`
            - `Bad Request: Invalid requirePullRequest value`
            - `Bad Request: deployPrivateKey: Invalid key format`
            - `Bad Request: deployPrivateKey: Wrong passphrase`
            - `Bad Request: deployPrivateKey: Encrypted key requires deployKeyPassphrase`
            - `Bad Request: deployKeyPassphrase cannot be provided without deployPrivateKey`
            - `Bad Request: deployPrivateKey not supported with authMethod: https_token`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Forbidden: Requires MANAGE_MODEL permission`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Not Found: Model does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: |
            Conflict

            Possible error messages:

            - `Conflict: Git configuration already exists for this model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    patch:
      tags:
        - Model Git configuration
      summary: Update Git configuration
      description: Update the Git configuration for a shared model, or a shared extension model in the **Develop independently** branch workflow. Only provided fields will be updated.
      security:
        - bearerAuth: []
      operationId: updateModelGitConfig
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the shared model, or a shared extension model in the **Develop independently** branch workflow.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                authMethod:
                  type: string
                  enum: [ssh, https_token]
                  description: |
                    The authentication method to change to:

                    - `ssh` for deploy key
                    - `https_token` for deploy token/PAT
                token:
                  type: string
                  maxLength: 1000
                  pattern: ^[a-zA-Z0-9_\\-.]+$
                  description: |
                    **Required when `authMethod: https_token`**. HTTPS token for authentication (deploy token value, PAT, etc.).
                sshUrl:
                  type: string
                  deprecated: true
                  description: |
                    **Deprecated. Use `cloneUrl` instead.** The SSH URL of the git repository.
                  example: "git@github.com:org/repo.git"
                baseBranch:
                  type: string
                  description: The target branch for Omni pull requests.
                  example: "main"
                branchPerPullRequest:
                  type: boolean
                  description: If `true`, all pull requests will create a branch in Omni, even those created outside of the tool.
                  example: false
                gitFollower:
                  type: boolean
                  description: If `true`, the shared model is read-only and can only be updated by merging pull requests to the `baseBranch`.
                  example: false
                gitServiceProvider:
                  type: string
                  enum: [auto, github, gitlab, azure_devops, bitbucket, bitbucket_datacenter]
                  description: |
                    The git provider type. Use `auto` for automatic detection based on the SSH URL.

                    - `auto` - Automatically detect the provider
                    - `github` - GitHub
                    - `gitlab` - GitLab
                    - `azure_devops` - Azure DevOps
                    - `bitbucket` - Bitbucket Cloud
                    - `bitbucket_datacenter` - Bitbucket Data Center (self-hosted)
                  example: "github"
                modelPath:
                  type: string
                  description: Path to model files within the repository.
                  example: "omni/blobs_r_us"
                requirePullRequest:
                  type: string
                  enum: [always, users-only, never]
                  description: |
                    Controls when pull requests are required for changes:

                    - `always` - Pull requests are required for all changes
                    - `users-only` - Pull requests are required only for user-initiated changes
                    - `never` - Pull requests are never required
                  example: "users-only"
                webUrl:
                  type: string
                  description: Custom web URL for the git repository. Use when the `cloneUrl` goes through a tunnel/VPC and differs from the inferred HTTPS address.
                  example: "https://github.com/org/repo"
                deployPrivateKey:
                  type: string
                  description: |
                    **Applicable to SSH authentication**. Supply your own RSA or ED25519 private key in PEM format (OpenSSH, PKCS#1, or PKCS#8) instead of using an Omni-generated keypair. Authorize the matching public key with your first, then set it here. The new key takes effect on the next git operation.
                  example: "-----BEGIN OPENSSH PRIVATE KEY-----\n..."
                deployKeyPassphrase:
                  type: string
                  description: |
                    **Applicable to SSH authentication.** Passphrase used to decrypt an encrypted `deployPrivateKey`. Omni uses it once to decrypt the key, then stores the key under its own encryption at rest. The passphrase itself is not retained.
                  example: "my-secure-passphrase"
            example:
              baseBranch: "develop"
              requirePullRequest: "always"
      responses:
        '200':
          description: Git configuration updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelGitConfig'
              example:
                authMethod: "ssh"
                baseBranch: "develop"
                branchPerPullRequest: false
                gitFollower: false
                gitServiceProvider: "github"
                modelPath: "omni/my_model"
                publicKey: "ssh-ed25519 AAAA..."
                requirePullRequest: "always"
                cloneUrl: "git@github.com:org/repo.git"
                webUrl: "https://github.com/org/repo"
                webhookUrl: "https://app.omni.co/api/webhooks/model/..."
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: modelId: Invalid uuid`
            - `Bad Request: Invalid cloneUrl format`
            - `Bad Request: Invalid requirePullRequest value`
            - `Bad Request: deployPrivateKey: Invalid key format`
            - `Bad Request: deployPrivateKey: Wrong passphrase`
            - `Bad Request: deployPrivateKey: Encrypted key requires deployKeyPassphrase`
            - `Bad Request: deployKeyPassphrase cannot be provided without deployPrivateKey`
            - `Bad Request: deployPrivateKey not supported with authMethod: https_token`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Forbidden: Requires MANAGE_MODEL permission`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Not Found: Model does not exist`
            - `Not Found: Git configuration not found for this model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Model Git configuration
      summary: Delete Git configuration
      description: Remove the Git configuration from a shared model, or a shared extension model in the **Develop independently** branch workflow.
      security:
        - bearerAuth: []
      operationId: deleteModelGitConfig
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the shared model, or a shared extension model in the **Develop independently** branch workflow.
      responses:
        '200':
          description: Git configuration deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: modelId: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Forbidden: Requires MANAGE_MODEL permission`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Not Found: Model does not exist`
            - `Not Found: Git configuration not found for this model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/git/sync:
    post:
      tags:
        - Model Git configuration
      summary: Sync model with Git repository
      description: |
        Trigger a sync between the model and its configured Git repository. Omni pulls the latest changes from the repository and, if the model has changes not yet in Git, pushes them in a commit using `commitMessage`.

        The `modelId` can be a shared model or a shared extension model. For an extension that uses the **Develop with the parent model** branch workflow, the sync runs on the parent model and includes every extension in that workflow. For an extension that uses the **Develop independently** branch workflow and is linked to its own Git repository, the sync runs on the extension's repository.
      security:
        - bearerAuth: []
      operationId: syncModelWithGit
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the shared model, or of a shared extension model. See the endpoint description for which repository is synced.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                commitMessage:
                  type: string
                  description: Optional commit message to use when pushing changes to the repository.
                  example: "Updated model configuration"
            example:
              commitMessage: "Updated model configuration"
      responses:
        '200':
          description: Sync operation completed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  didSync:
                    type: boolean
                    description: Whether a sync was performed. `false` if the model was already in sync.
                    example: true
                  gitSha:
                    type: string
                    nullable: true
                    description: The git SHA after the sync operation, or null if no sync was performed.
                    example: "abc123def456"
                  inSync:
                    type: boolean
                    description: Whether the model is currently in sync with the git repository.
                    example: true
                  message:
                    type: string
                    description: Human-readable status message describing the sync result.
                    example: "Model synced successfully"
              example:
                didSync: true
                gitSha: "abc123def456"
                inSync: true
                message: "Model synced successfully"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: modelId: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Forbidden: Requires MANAGE_MODEL permission`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Not Found: Model does not exist`
            - `Not Found: Git configuration not found for this model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/git/commit:
    post:
      tags:
        - Model Git configuration
      summary: Create or update a pull request for a model branch
      description: |
        <Note>
          This endpoint requires **Modeler** or **Connection Admin** permissions.
        </Note>

        Push a branch's model contents to Git and create or update a pull request. This mirrors the **Create Pull Request** / **Update Pull Request** buttons in Omni allowing API consumers and AI agents to drive the pull request workflow programmatically.

        By default, the endpoint automatically detects whether the Git branch exists. If a branch doesn't exist, a successful request will create a new Git branch and open a pull request. If a branch does exist, a commit will be added to the branch.

        The `allow_branch_exists` and `require_branch_exists` parameters constrain this behavior:

        - **Create or update mode** (default) - Auto-detects and handles both scenarios
        - **Create-only mode** (`allow_branch_exists: false`) - Fails if the Git branch already exists
        - **Update-only mode** (`require_branch_exists: true`) - Fails if the Git branch doesn't exist
      security:
        - bearerAuth: []
      operationId: commitModelBranch
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the shared model, or a shared extension model in the **Develop independently** branch workflow.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - branch_id
                - commit_message
              properties:
                branch_id:
                  type: string
                  format: uuid
                  description: UUID of the Omni branch to commit.
                  example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                commit_message:
                  type: string
                  description: Commit message for the git commit.
                  example: "Add new customer dimension"
                allow_branch_exists:
                  type: boolean
                  default: true
                  description: |
                    Set `false` to fail if git branch already exists (**Create-only mode**).
                  example: true
                require_branch_exists:
                  type: boolean
                  default: false
                  description: |
                    Set `true` to fail if git branch doesn't exist (**Update-only mode**).
                  example: false
            example:
              branch_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
              commit_message: "Add new customer dimension"
              allow_branch_exists: true
              require_branch_exists: false
      responses:
        '200':
          description: Commit operation completed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  pr_url:
                    type: string
                    description: URL of the created or updated pull request.
                    example: "https://github.com/myorg/repo/compare/main...feature?expand=1"
                  git_sha:
                    type: string
                    description: The git SHA of the commit.
                    example: "abc123def456"
                  in_sync:
                    type: boolean
                    description: Whether the shared model is currently in sync with its configured default git branch.
                    example: true
                  did_sync:
                    type: boolean
                    description: Whether a sync operation was performed between the shared model and its configured default git branch.
                    example: true
              example:
                pr_url: "https://github.com/myorg/repo/compare/main...feature?expand=1"
                git_sha: "abc123def456"
                in_sync: true
                did_sync: true
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: modelId: Invalid uuid`
            - `Bad Request: branch_id: Invalid uuid`
            - `Bad Request: commit_message is required`
            - `Bad Request: Cannot set both allow_branch_exists=false and require_branch_exists=true`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Forbidden: Requires MANAGE_MODEL permission` - This endpoint requires **Modeler** or **Connection Admin** permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Not Found: Model does not exist`
            - `Not Found: Branch does not exist`
            - `Not Found: Git configuration not found for this model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/git/rotate-webhook-secret:
    post:
      tags:
        - Model Git configuration
      operationId: modelsGitRotateWebhookSecret
      summary: Rotate webhook secret
      description: |
        <Note>
          This endpoint requires Modeler permissions or higher.
        </Note>

        Rotate the pull request webhook signing secret for the Git configuration of a shared model, or a shared extension model in the **Develop independently** branch workflow, and return the updated Git configuration with the new secret. The previous secret stops validating webhook deliveries immediately.

        If the git repository is shared across multiple models, the secret belongs to whichever model owns the shared configuration. Rotating the secret for any of the sharing models rotates the one shared secret for all of them.

        This is the only endpoint that returns the new secret value. Call `GET /v1/models/{modelId}/git?include=webhookSecret` to retrieve it after the rotation, if needed.
      security:
        - bearerAuth: []
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the shared model, or a shared extension model in the **Develop independently** branch workflow.
      responses:
        '200':
          description: Webhook secret rotated successfully. The response includes the new webhook secret.
          content:
            application/json:
              schema:
                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
              example:
                authMethod: "ssh"
                baseBranch: "main"
                branchPerPullRequest: false
                gitFollower: false
                gitServiceProvider: "github"
                modelPath: "omni/blobs_r_us"
                publicKey: "ssh-ed25519 AAAA..."
                requirePullRequest: "users-only"
                cloneUrl: "git@github.com:org/repo.git"
                webUrl: "https://github.com/org/repo"
                webhookUrl: "https://app.omni.co/api/webhooks/model/..."
                webhookSecret: "whsec_new_secret_value"
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Forbidden: Requires MANAGE_MODEL permission`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Not Found: Model does not exist`
            - `Not Found: Git configuration not found for this model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '422':
          description: |
            Unprocessable Entity

            Possible error messages:

            - `Model is not a shared model or a shared extension with isolated branches`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/migrate:
    post:
      tags:
        - Models
      summary: Migrate a model
      description: |
        Copy a model from one Omni connection to another by reading the source model's YAML at a specific Git ref and writing it to the target model. Supports same-organization and cross-organization migrations.

        This API:

        1. Reads the full model YAML from the source model's git repository at the specified `gitRef` (merged with the default branch).
        2. If `branchName` is provided and the branch already exists on the target model, the API writes to that branch. If the branch doesn't exist, one is created.
        3. Writes the YAML to the target model or branch, replacing its model definition.

        ### Requirements

        To successfully migrate a model:

        - The user performing the migration must have:
           - **For the source model** - **Querier**, **Modeler**, or **Connection Admin** permissions 
           - **For the target model** - **Modeler** or **Connection Admin** permissions
           - **For cross-organization migrations** - The user must be a member of both organizations
        - The source model must have [git configured](/integrations/git). This is required so the API can read the model YAML from the repository.
        - The target model should have an identical schema model to the source model at the instant the git ref was committed.
      security:
        - orgApiKey: []
      operationId: migrateModel
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The shared model ID to read YAML from.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - gitRef
                - targetModelId
              properties:
                gitRef:
                  type: string
                  description: Git reference (branch name, tag, or commit SHA) to read the source model YAML from.
                  example: "main"
                targetModelId:
                  type: string
                  format: uuid
                  description: The shared model ID to write the YAML to.
                  example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                branchName:
                  type: string
                  description: |
                    **Required if the target model has git enabled.**. Branch name on the target model. If the branch doesn't exist, it will be created.
                  example: "migrate-from-prod"
                commitMessage:
                  type: string
                  description: Git commit message.
                  example: "Migrate model from production"
                deleteViewsAndTopicsMissingFromSource:
                  type: boolean
                  description: |
                    Controls how views and topics present in the target model but missing from the source model are handled. When `true` (default), these views and topics are marked as `ignored: true`. When `false`, they are inherited from the parent model instead.
                  default: true
                  example: true
            example:
              gitRef: "main"
              targetModelId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
              branchName: "migrate-from-prod"
              commitMessage: "Migrate model from production"
              deleteViewsAndTopicsMissingFromSource: true
      responses:
        '200':
          description: Model migrated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Whether the migration completed successfully.
                    example: true
              example:
                success: true
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: Invalid JSON body`
            - `Bad Request: gitRef is empty`
            - `Bad Request: targetModelId is not a valid UUID`
            - `Bad Request: Target model has git enabled but branchName was not provided`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Forbidden: User does not have access to the source or target model`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Not Found: Source model not found`
            - `Not Found: Target model not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/query/run:
    post:
      tags:
        - Queries
      summary: Run query
      description: |
        Run the semantic query specified in the request body. Successful requests will return the results according to the specified [`resultType`](#results).
      x-mint:
        content: |
          ## Specifying result types {#results}

          The `resultType` parameter determines how the API returns query results:
          
          - **When set**, the response is a single CSV, XLSX, or JSON document.
          - **When not set**, the response is a stream of newline-delimited JSON, one JSON object per line: a `jobs_submitted` header, then one line per job as it reaches a terminal state. A completed job contains the result set as base64-encoded Arrow IPC in `result`; use `summary.fields` to interpret the decoded columns.

             The response will also contain a `footer`. A footer with non-empty `remaining_job_ids` means the wait window elapsed before every job finished. See **Timeouts** below for more information.

          ## Timeouts

          If a request takes too long, the API will return a `408 timeout` error with a `remaining_job_ids` property. When this occurs, poll the [Wait for query results](/api/queries/wait-for-query-results) endpoint until the `timed_out` property is `false`.

          ## Reading large results {#reading-large-results}

          A large result streamed inline can take minutes and is lost if the connection drops. To read it in chunks instead:

          1. Call this endpoint (and, if needed, [Wait for query results](/api/queries/wait-for-query-results)) with `completionOnly: true` so completed job lines omit `result`.
          2. Once a job's `status` is `COMPLETE`, call [Read query results by offset](/api/queries/read-query-results-by-offset) with `offset=0`.
          3. Call it again with `offset` set to the previous response's `stream_stats.next_offset`, repeating until `next_offset` is `null`.
          4. Each response's `result` is base64 of one raw slice of the Arrow IPC stream — decode and append the slices in order, then parse the joined bytes. 
          
             The results endpoint's `timeoutMs` parameter stops a read early at a chunk boundary (at least one chunk is always returned), so a dropped connection can resume from the last `next_offset` you received. Results stay readable for about an hour after the job completes.
      security:
        - bearerAuth: []
      operationId: runQuery
      parameters:
        - name: userId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: |
            **Requires an Organization API key.** The ID of the user to run the query as. Prefer using this parameter to specify a user over the `userId` request body parameter.

            If not provided as a query parameter or in the request body, the API runs the query as the calling user. 

            Personal Access Tokens (PATs) cannot use this parameter to act on behalf of other users.
          example: 9e8719d9-276a-4964-9395-a493189a247c
      requestBody:     # QueryRunBody
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - query
              properties:
                query:  # ApiQueryWithNullableLimit
                  type: object
                  description: |
                    A JSON object representing the query to be run.

                    To retrieve a query's JSON object from an Omni workbook:

                    1. Open a workbook in Omni.
                    2. Click the **bug icon** to toggle open the **Inspector** panel. For **Mac** use `Option + 9`; for **Windows**, use `Alt + 9`.
                    3. Locate the **Query structure** section.
                    4. Copy the query JSON.

                    `branchId` belongs at the top level of the request body, not inside `query`. A `branchId` or `branch_id` nested inside `query` is rejected.
                  required:
                    - fields
                    - modelId
                  properties:
                    modelId:
                      type: string
                      format: uuid
                      description: The ID of the model to execute the query against.
                    table:
                      type: string
                      description: The base table or topic name.
                    fields:
                      type: array
                      items:
                        type: string
                      description: The column names to include in the query results.
                    limit:
                      oneOf:
                        - type: integer
                          default: 1000
                        - type: "null"
                      description: |
                        Number of rows to return. Defaults to `1000` if not defined.

                        - If `null`, unlimited results will be returned. `null` is rejected for a pivoted query unless `resultType` is set.
                        - If set to a negative number, the API will return a `400` response.
                    sorts:
                      type: array
                      items:
                        type: object
                        properties:
                          column_name:
                            type: string
                            description: The column to sort by
                          sort_descending:
                            type: boolean
                            description: If `true`, sort in descending order
                          is_column_sort:
                            type: boolean
                          null_sort:
                            type: string
                            description: How to handle nulls (e.g., `OMNI_DEFAULT`)
                      description: Sort specifications for the query results.
                    filters:
                      type: object
                      description: Filter conditions to apply to the query.
                    pivots:
                      type: array
                      items:
                        type: object
                      description: Pivot configurations for the query.
                    calculations:
                      type: array
                      items:
                        type: object
                      description: Custom calculations to include in the query.
                    column_totals:
                      type: object
                      description: Column total configuration.
                    row_totals:
                      type: object
                      description: Row total configuration.
                    column_limit:
                      type: integer
                      description: Column limit for pivoted queries.
                    join_paths_from_topic_name:
                      type: string
                      description: Name of the topic the query runs on; it also sets join path precedence.
                    join_via_map:
                      type: object
                      description: Custom join path mappings.
                    default_group_by:
                      type: boolean
                      description: If `true`, enable default grouping behavior.
                    fill_fields:
                      type: array
                      items:
                        type: string
                      description: A list of dimensions that should have missing values filled with placeholder rows.
                    custom_summary_types:
                      type: object
                      description: Custom summary type configurations.
                    model_extension_id:
                      type: string
                      description: ID of a model extension layered on top of the base model.
                    offset:
                      type: integer
                      description: Row offset for pagination.
                    period_over_period_computations:
                      type: array
                      items:
                        type: object
                      description: Period-over-period comparison configurations.
                    transposed_measures:
                      type: array
                      items:
                        type: string
                      description: Measure field names that have been transposed into rows.
                branchId:
                  type: string
                  format: uuid
                  description: |
                    Optional ID of a model branch to execute the query against. When provided, the query runs against the specified branch instead of the shared model. 

                    The branch must belong to the same shared model specified in `query.modelId` and be within your organization.

                    Takes precedence over the legacy `?branch_id=` URL query parameter.
                environmentConnectionId:
                  type: string
                  format: uuid
                  description: |
                    Connection ID of the environment to run the query against, overriding the connection environment inherited from the calling user's (or target user, if `userId` is provided) session or default. The requesting user must have access to the specified environment.
                userId:
                  type: string
                  format: uuid
                  description: |
                    **Prefer passing `userId` as a query parameter instead of in the request body.** Providing `userId` in both locations will return a `400` error.

                    **Requires an Organization API key**. Executes the query as the specified user. When set, the user's attributes are applied for row-level security and connection-environment switching. The `userId` must belong to a user in your organization.

                    If not provided as a query parameter or in the request body, the API uses the user associated with the API token. 
                cache:
                  type: string
                  default: SkipRequery
                  enum: [ Standard, SkipRequery, SkipCache, SkipCacheAndRebuildExtracts ]
                  description: |
                    Cache policy to control how query caching behaves. Must be one of:

                    - `Standard` - Uses standard caching behavior.
                    - `SkipRequery` - **Default**. Uses cached results if available, but does not requery if not found.
                    - `SkipCache` - Bypasses cache and always executes a fresh query.
                    - `SkipCacheAndRebuildExtracts` - Same as `SkipCache`, and also rebuilds any extracts the query depends on.
                completionOnly:
                  type: boolean
                  default: false
                  description: |
                    **Cannot be used with `resultType`.** If `true`, completed job lines omit `result`; read the data afterward with the [Read query results by offset](/api/queries/read-query-results-by-offset) endpoint, which can page a large result by byte offset. See [Reading large results](#reading-large-results) for more information.
                resultType:
                  type: string
                  enum: [ csv, json, xlsx ]
                  description: |
                    **Cannot be used with `planOnly`.** Specifies the format of query results exported by the API.

                    If omitted, the response is a stream of newline-delimited JSON. See [Specifying result types](#results) for more information.

                    For `csv` and `xlsx`, if the query has subtotal or grand total rows, one dimension cell in each totals row is labeled `Total`. This doesn't apply to the base-64 encoded format or `json`.
                planOnly:
                  type: boolean
                  description: |
                    **Cannot be used with `resultType`.** If `true`, returns only the query execution plan without running the query. The response will include the generated SQL and query metadata.
                formatResults:
                  type: boolean
                  default: true
                  description: |
                    **Applicable only if `resultType` is specified.** If `true`, formatting will be applied to numeric and currency values in the form of currency symbols and thousand separators.
                timezone:
                  type: string
                  description: |
                    IANA timezone identifier, such as `America/Los_Angeles`, that overrides the user's configured timezone for this query. Only takes effect when the connection has user-specific timezones enabled.
                workbookUrl:
                  type: boolean
                  default: false
                  description: |
                    **Cannot be combined with `planOnly`.** If `true`, creates an ephemeral workbook reproducing the query (preserving the run's `branchId` and timezone) and returns its URL in the `X-Omni-Workbook-Url` response header. The header works for all `resultType` modes and is delivered as a header to keep response bodies byte-identical.

                    The header is best-effort: it will be silently omitted if the (target) user lacks the workbooks permission (`USE_WORKBOOKS`) on the query's model, or if workbook creation fails.
            example:
              query:
                modelId: "bcf0cffd-ec1b-44d5-945a-a261ebe407fc"
                table: "order_items"
                fields:
                  - "inventory_items.product_department"
                  - "inventory_items.product_category"
                  - "inventory_items.count"
                limit: 10
                sorts:
                  - column_name: "inventory_items.product_department"
                    sort_descending: false
                join_paths_from_topic_name: "order_items"
      responses:
        '200':
          description: |
            Query executed or started successfully. The result type is determined by the `resultType` parameter. See [Specifying result types](#results) for more information.

            **Use the type dropdown to the right of the Response header to view an example of each response type:**

            ![Response type dropdown menu](/api/images/response-type-menu.png)
          headers:
            X-Omni-Workbook-Url:
              description: |
                URL of an ephemeral workbook reproducing this query, preserving the run's branch and timezone. Present only when the request sets `workbookUrl: true` and the (target) user has the permissions to use workbooks on the query's model.

                The workbook provides a "view in Omni" jump from API query results to the live data. Workbook creation is best-effort — if permission is missing or creation fails, the query still succeeds and this header is silently omitted.
              schema:
                type: string
                format: uri
                example: "https://myorg.omniapp.co/e/1:abc123DEF456/1"
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: {}
                description: |
                  Array of result-row objects keyed by field name. Returned only when `resultType` is `json`.
              examples:
                jsonResults:
                  summary: JSON results
                  value:
                    - inventory_items.product_department: "Women"
                      inventory_items.product_category: "Jeans"
                      inventory_items.count: 42
                    - inventory_items.product_department: "Men"
                      inventory_items.product_category: "Outerwear & Coats"
                      inventory_items.count: 17
            application/vnd.ms-excel:
              schema:
                description: |
                  XLSX document. Returned only when `resultType` is `xlsx`.
                format: binary
                type: string
            text/csv:
              schema:
                description: |
                  CSV document. Returned only when `resultType` is `csv`.
                type: string
              examples:
                csvResults:
                  summary: CSV results
                  value: |
                    inventory_items.product_department,inventory_items.product_category,inventory_items.count
                    Women,Jeans,42
                    Men,Outerwear & Coats,17
            text/ndjson:
              schema:  # QueryWaitStreamLine
                description: |
                  One line of the query/run NDJSON stream: a `jobs_submitted` header, then one line per job, then a footer.
                anyOf:  
                  - type: object # QueryStreamJobsSubmittedLine 
                    required:
                      - jobs_submitted
                    description: |
                      First line of the query/run stream: the jobs accepted for execution.
                    properties:
                      jobs_submitted:
                        type: object
                        additionalProperties:
                          type: string
                          nullable: true
                          format: uuid
                        description: |
                          Map of submitted job ID to the client result ID for that job. Null when the job has no client result ID. 
                          
                          Job IDs are the keys to poll the [Wait for query results](/api/queries/wait-for-query-results) endpoint.
                  - $ref: '#/components/schemas/QueryStreamJobLine'
                  - $ref: '#/components/schemas/QueryStreamFooterLine'
              examples:
                ndjsonResults:
                  summary: NDJSON stream
                  value: |
                    {"jobs_submitted": {"a1b2c3d4-e5f6-7890-abcd-ef1234567890": null}}
                    {"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "COMPLETE", "result": "QVJST1cxAAD/////...", "summary": {"fields": {"inventory_items.product_department": {}, "inventory_items.product_category": {}, "inventory_items.count": {}}}}
                    {"remaining_job_ids": [], "timed_out": "false"}
        '400':
          description: |
            Bad Request. Possible causes:

            - `userId` provided in both the query parameter and request body
            - `formatResults` provided without `resultType`
            - `planOnly` and `resultType` both provided (incompatible)
            - `completionOnly` and `resultType` both provided (incompatible)
            - `planOnly` and `workbookUrl` both provided (incompatible)
            - `workbookUrl` requested on a query containing `staticQueryReferences`
            - `query.limit` is a negative number
            - `branchId` provided when querying a schema model (schema models do not have branches)
            - `branchId` is not a valid UUID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              examples:
                userIdInBothLocations:
                  summary: userId in both query parameter and request body
                  value:
                    detail: "`userId` may be provided in either the request body or as a query parameter, but not both"
                    status: 400
                formatResultsWithoutResultType:
                  summary: formatResults without resultType
                  value:
                    detail: "`formatResults` cannot be provided without `resultType`"
                    status: 400
                planOnlyWithResultType:
                  summary: planOnly and resultType both provided
                  value:
                    detail: "`planOnly` and `resultType` cannot both be provided"
                    status: 400
                completionOnlyWithResultType:
                  summary: completionOnly and resultType both provided
                  value:
                    detail: "`completionOnly` and `resultType` cannot both be provided"
                    status: 400
                negativeLimitValue:
                  summary: Negative limit value
                  value:
                    detail: "`query.limit`: Too small: expected number to be >0"
                    status: 400
                schemaModelWithBranch:
                  summary: Schema model with branchId
                  value:
                    detail: "Schema models do not have branches"
                    status: 400
                planOnlyWithWorkbookUrl:
                  summary: planOnly and workbookUrl both provided
                  value:
                    detail: "`planOnly` and `workbookUrl` cannot both be provided"
                    status: 400
                workbookUrlWithStaticQueryReferences:
                  summary: |
                    `workbookUrl` requested on a query with `staticQueryReferences`
                  value:
                    detail: "workbookUrl cannot be requested on queries containing staticQueryReferences"
                    status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Forbidden. Possible causes:

            - A Personal Access Token (PAT) attempted to use `userId` to run the query as a different user. PATs can only run queries as their own user.
            - If including a `connectionEnvironmentId`, the requesting user must have access to the specified environment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              examples:
                userScopedKeyDifferentUser:
                  summary: PAT attempting to use different userId
                  value:
                    detail: "Personal Access Tokens cannot run queries on behalf of other users"
                    status: 403
        '404':
          description: |
            Not Found. Possible causes:

            - The specified `userId` does not exist or does not belong to your organization
            - The specified model, topic, or view does not exist
            - The specified `branchId` does not exist, belongs to a different model, or is in a different organization
            - The branch has been soft-deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              examples:
                userNotFound:
                  summary: User not found
                  value:
                    detail: "User with id 550e8400-e29b-41d4-a716-446655440000 does not exist"
                    status: 404
                branchNotFound:
                  summary: Branch not found
                  value:
                    detail: "Branch model with id 550e8400-e29b-41d4-a716-446655440000 does not exist"
                    status: 404
        '408':
          description: |
            Query timed out. Only returned when `resultType` is `csv`, `json`, or `xlsx`. 
            
            The default `text/ndjson` response reports timeouts via the footer line's `remaining_job_ids` instead. The response includes `remaining_job_ids` that can be polled with the [Wait for query results endpoint](/api/queries/wait-for-query-results) endpoint.
          $ref: '#/components/responses/QueryTimeoutError'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/query/wait:
    get:
      tags:
        - Queries
      summary: Wait for query results
      description: |
        Poll for the results of one or more query jobs. Use this endpoint when a request to the [Run query endpoint](/api/queries/run-query) times out and returns `remaining_job_ids` in the response.

        This endpoint waits for the specified jobs to complete and returns their results as a stream of newline-delimited JSON: one line per completed job, followed by a footer line.

        If the footer's `remaining_job_ids` is not empty, call this endpoint again with those IDs until the list is empty and `timed_out` is `"false"`.
      security:
        - bearerAuth: []
      operationId: waitForQuery
      parameters:
        - name: completionOnly
          in: query
          required: false
          schema:
            type: string
            enum: ['true', 'false']
          description: |
            If `"true"`, completed job lines omit `result`; read the data afterward with the [Read query results by offset](/api/queries/read-query-results-by-offset) endpoint. See [Reading large results](/api/queries/run-query#reading-large-results) for more information.
        - name: jobIds
          in: query
          required: false
          schema:
            type: string
            example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890,b2c3d4e5-f6a7-8901-bcde-f12345678901"
          description: |
            **Required unless the deprecated `job_ids` parameter is sent**. A comma-separated list of job UUIDs to poll for results. These IDs are returned in the `remaining_job_ids` property when a request to the [Run query endpoint](/api/queries/run-query) times out.

            For example, `?jobIds=a1b2c3d4-e5f6-7890-abcd-ef1234567890,b2c3d4e5-f6a7-8901-bcde-f12345678901`. A value that is not a valid UUID returns a `400` error.
        - name: job_ids
          in: query
          required: false
          deprecated: true
          schema:
            type: string
          description: |
            **Deprecated:** Use `jobIds` instead.
      responses:
        '200':
          description: |
            Query results for completed jobs, streamed as newline-delimited JSON (`text/ndjson`): one object per line. The response as a whole cannot be parsed as a single JSON document — parse it line by line.

            The stream ends with a footer line. If the footer's `remaining_job_ids` is not empty, continue polling this endpoint with those IDs until the list is empty.
          content:
            text/ndjson:
              schema: # QueryWaitStreamLine
                description: One line of the [Wait for query results](/api/queries/wait-for-query-results) NDJSON stream — one line per completed job, then a footer line.
                anyOf:  
                  - $ref: '#/components/schemas/QueryStreamJobLine'
                  - $ref: '#/components/schemas/QueryStreamFooterLine'
              examples:
                completedJob:
                  summary: Job line for a completed job
                  value:
                    job_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    status: "COMPLETE"
                    client_result_id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    summary:
                      sql: "SELECT * FROM order_items LIMIT 10"
                      total_rows: 10
                      execution_time_ms: 245
                    result: "QVJST1cxAAD/////..."
                footer:
                  summary: Footer line with no remaining jobs
                  value:
                    remaining_job_ids: []
                    timed_out: "false"
        '400':
          description: |
            Bad Request. Possible causes:

            - Neither `jobIds` nor `job_ids` was provided
            - `jobIds` or `job_ids` contains a value that is not a valid UUID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missingJobIds:
                  summary: Missing job_ids parameter
                  value:
                    detail: "job_ids parameter is required"
                    status: 400
                invalidJobIds:
                  summary: Invalid jobIds format
                  value:
                    detail: "jobIds must contain valid UUIDs"
                    status: 400
                invalidLegacyJobIds:
                  summary: Invalid job_ids format
                  value:
                    detail: "job_ids must be a valid JSON array of UUIDs"
                    status: 400
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible causes:

            - One or more job IDs do not exist
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                jobNotFound:
                  summary: Job not found
                  value:
                    detail: "Job with id <jobId> does not exist"
                    status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          description: Error fetching query results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/query/results:
    get:
      tags:
        - Queries
      summary: Read query results by offset
      description: |
        Read a completed job's stored result by raw byte offset. See [Reading large results](/api/queries/run-query#reading-large-results) for the full flow with `completionOnly`.

        Results stay readable for about an hour after the job completes. Reading a job that hasn't completed yet, or whose result has expired, returns an error status on the job line.
      security:
        - bearerAuth: []
      operationId: readQueryResults
      parameters:
        - name: jobId
          in: query
          required: true
          schema:
            type: string
            format: uuid
            example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          description: |
            ID of a completed query job, from the [Run query](/api/queries/run-query) or [Wait for query results](/api/queries/wait-for-query-results) response. Only one job can be read per request.
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
          description: |
            Raw result byte to start reading from: `0`, or the previous response's `stream_stats.next_offset`.
        - name: maxBytes
          in: query
          required: false
          schema:
            type: integer
            exclusiveMinimum: 0
            example: 50000000
          description: |
            Most raw result bytes to return. Omit to read to the end of the result.
        - name: timeoutMs
          in: query
          required: false
          schema:
            type: integer
            exclusiveMinimum: 0
            example: 60000
          description: |
            Stop streaming after roughly this many milliseconds; `stream_stats.next_offset` says where to resume. At least one chunk is always returned, so a slow read can't stall at the same offset.
      responses:
        '200':
          description: |
            The requested byte window of the job's result, streamed as newline-delimited JSON (`text/ndjson`): one job line, then a footer line. The response as a whole cannot be parsed as a single JSON document — parse it line by line.
          content:
            text/ndjson:
              schema: # QueryWaitStreamLine
                description: One job line including the requested slice in `result` and the byte window in `stream_stats`, then a footer line.
                anyOf:
                  - $ref: '#/components/schemas/QueryStreamJobLine'
                  - $ref: '#/components/schemas/QueryStreamFooterLine'
              examples:
                jobLine:
                  summary: Job line for a paged read
                  value:
                    job_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    status: "COMPLETE"
                    result: "QVJST1cxAAD/////..."
                    stream_stats:
                      offset: 0
                      bytes: 25000000
                      total_bytes: 60000000
                      next_offset: 25000000
        '400':
          description: |
            Bad Request. Possible causes:

            - `jobId` is missing or not a valid UUID
            - `offset` is negative or not an integer
            - `maxBytes` or `timeoutMs` is zero, negative, or not an integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                invalidOffset:
                  summary: Negative offset
                  value:
                    detail: "Bad Request: offset: Too small: expected number to be >=0"
                    status: 400
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          description: Error reading query results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/schedules:
    post:
      tags:
        - Schedules
      summary: Create schedule
      description: |
        Create a scheduled task delivering a document's dashboard, or one tab of its workbook with `entity: workbook`. Supports applying filters and formatting, creating alert conditions, and triggering test deliveries.

        Supports the following destinations:

        - [**Email**](/share/deliveries/email)
        - [**Slack**](/share/deliveries/slack) - **Note**: A Slack workspace must already be connected to Omni to use this destination type.
        - [**SFTP**](/share/deliveries/sftp)
        - [**Webhook**](/share/deliveries/webhooks)
        - [**Amazon S3**](/share/deliveries/amazon-s3)
      security:
        - bearerAuth: []
      operationId: createSchedule
      parameters:
        - name: userId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            **Only supported when using an Organization API key.** The membership ID of the user who should own the schedule. The specified user must have permission to view and schedule the document, not just view it. **Note**: This value is the **membership ID**, not the user ID. Use the [List users](/api/users/list-users) endpoint to retrieve membership IDs.

            When using an Organization API key:

            - If `userId` is provided, the schedule is owned by the specified user
            - If `userId` is not provided, the schedule is owned by the organization

            Attempting to use a Personal Access Token (PAT) with this parameter will result in a `403 Forbidden` error. When using a PAT, the schedule owner will be the creator of the PAT.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - type: object
                  properties:
                    name:
                      type: string
                      minLength: 1
                      description: The name of the scheduled task. Must be at least 1 character.
                      example: Weekly Sales Report
                    schedule:
                      type: string
                      description: 'When the task runs, as an AWS EventBridge cron expression: minute hour day-of-month month day-of-week year. Runs cannot be more frequent than once an hour unless the organization allows a higher frequency. Delivery times are approximate and happen within 15 minutes of the scheduled time.'
                      example: 0 9 ? * MON *
                    timezone:
                      type: string
                      description: The IANA timezone for interpreting the schedule. Must be a valid IANA timezone identifier.
                      example: America/New_York
                    timezoneOverride:
                      type: string
                      nullable: true
                      description: Optional IANA timezone applied to query execution at render time. Distinct from `timezone` (which controls *when* the schedule fires). Pass `null` to clear it (use the default).
                      example: Europe/Paris
                    entity:
                      type: string
                      enum: [dashboard, workbook]
                      default: dashboard
                      description: |
                        What to deliver: the document's dashboard, or a tab of its workbook. Defaults to `dashboard`.

                        With `entity: workbook`, `queryIdentifierMapKey` is required to name the tab, and `filterConfig` and `singleColumnLayout` aren't supported.
                    identifier:
                      type: string
                      description: |
                        The ID of the document where the task will be created. This identifier is shared by the document's dashboard and workbook. This can be retrieved by:

                        - **Opening the document settings** - Navigate to **File > Settings...** in the dashboard. The **Identifier** field contains the document's ID.
                        - **Using the dashboard's URL** - The string after `/dashboards` is the document's ID; for example: `https://myorg.omniapp.co/dashboards/12db1a0a`
                  required:
                    - name
                    - schedule
                    - timezone
                    - identifier
                - oneOf:
                    - title: Email
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleExpandTablesToShowAllRows'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        testNow:
                          type: boolean
                          default: false
                          description: 'If true, run the task immediately instead of scheduling. Not supported for `RESULTS_CHANGED` and `RESULTS_UNCHANGED` alert conditions, or for `s3` destinations: create the schedule first, then use the [trigger endpoint](/api/schedules/trigger-schedule).'
                          example: false
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        destinationType:
                          type: string
                          enum:
                            - email
                          description: The destination type for delivering to email recipients.
                          example: email
                        fanOut:
                          $ref: '#/components/schemas/ScheduleEmailFanOut'
                        filename:
                          type: string
                          description: Optional custom filename for the attachment (without extension). Must not contain special characters.
                          example: weekly-sales-report
                        recipients:
                          anyOf:
                            - type: string
                              minLength: 1
                            - type: array
                              items:
                                type: string
                              minItems: 1
                            - type: object
                              properties:
                                emails:
                                  type: array
                                  items:
                                    type: string
                                  default: []
                                userGroupIds:
                                  type: array
                                  items:
                                    type: string
                                    format: uuid
                                  default: []
                          description: Email recipients. Can be email addresses or user group IDs (if allowed).
                        subject:
                          type: string
                          minLength: 1
                          description: The subject line of the email. Must be at least 1 character.
                          example: Weekly Sales Report
                        textBody:
                          type: string
                          description: Optional plain text body content for the email.
                          example: Please find attached the weekly sales report.
                      required:
                        - format
                        - destinationType
                        - recipients
                        - subject
                    - title: Webhook
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleExpandTablesToShowAllRows'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        testNow:
                          type: boolean
                          default: false
                          description: 'If true, run the task immediately instead of scheduling. Not supported for `RESULTS_CHANGED` and `RESULTS_UNCHANGED` alert conditions, or for `s3` destinations: create the schedule first, then use the [trigger endpoint](/api/schedules/trigger-schedule).'
                          example: false
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        destinationType:
                          type: string
                          enum:
                            - webhook
                          description: The destination type for delivering to a webhook URL.
                          example: webhook
                        filename:
                          type: string
                          description: Optional custom filename for the webhook payload.
                          example: report-data
                        url:
                          $ref: '#/components/schemas/ScheduleWebhookUrl'
                      required:
                        - format
                        - destinationType
                        - url
                    - title: SFTP
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleExpandTablesToShowAllRows'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        testNow:
                          type: boolean
                          default: false
                          description: 'If true, run the task immediately instead of scheduling. Not supported for `RESULTS_CHANGED` and `RESULTS_UNCHANGED` alert conditions, or for `s3` destinations: create the schedule first, then use the [trigger endpoint](/api/schedules/trigger-schedule).'
                          example: false
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        address:
                          type: string
                          description: |
                            The SFTP server address including the path, in `sftp://hostname/path/to/directory` format.
                          example: sftp://sftp.example.com/reports/weekly
                        credentialId:
                          type: string
                          description: |
                            **Required if neither `passwordUnencrypted` nor `privateKeyUnencrypted` is provided.** The ID of a stored credential to use for SFTP authentication.
                          example: sftp-cred-123
                        destinationType:
                          type: string
                          enum:
                            - sftp
                          description: The destination type for delivering to an SFTP server.
                          example: sftp
                        filename:
                          type: string
                          description: Optional custom filename for the uploaded file (without extension).
                          example: weekly-report
                        passwordUnencrypted:
                          type: string
                          minLength: 1
                          description: |
                            **Required if `credentialId` is not provided.** The password for SFTP authentication. Can't be combined with `privateKeyUnencrypted`.
                          example: secretpassword
                        port:
                          type: number
                          minimum: 1
                          maximum: 65535
                          description: The SFTP server port. Standard SFTP port is 22.
                          example: 22
                        privateKeyPassphraseUnencrypted:
                          type: string
                          description: |
                            **Required if `privateKeyUnencrypted` is provided**. The passphrase for `privateKeyUnencrypted`, if the key is encrypted.
                        privateKeyUnencrypted:
                          type: string
                          minLength: 1
                          description: |
                            **Required if neither `credentialId` nor `passwordUnencrypted` is provided.** The SSH private key for SFTP authentication, in one of the following formats:
                            
                            - **OpenSSH** - `-----BEGIN OPENSSH PRIVATE KEY-----`
                            - **PEM RSA/EC format** - `-----BEGIN RSA PRIVATE KEY-----`, `-----BEGIN EC PRIVATE KEY-----`). 
                            
                            PKCS#8 keys (`-----BEGIN PRIVATE KEY-----`) aren't supported. 
                            
                            Can't be combined with `passwordUnencrypted`.
                          example: |-
                            -----BEGIN OPENSSH PRIVATE KEY-----
                            ...
                            -----END OPENSSH PRIVATE KEY-----
                        username:
                          $ref: '#/components/schemas/ScheduleSftpUsername'
                      required:
                        - format
                        - address
                        - destinationType
                        - port
                        - username
                    - title: Slack
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleExpandTablesToShowAllRows'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        testNow:
                          type: boolean
                          default: false
                          description: 'If true, run the task immediately instead of scheduling. Not supported for `RESULTS_CHANGED` and `RESULTS_UNCHANGED` alert conditions, or for `s3` destinations: create the schedule first, then use the [trigger endpoint](/api/schedules/trigger-schedule).'
                          example: false
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        destinationType:
                          type: string
                          enum:
                            - slack
                          description: The destination type for delivering to Slack. The organization must have a Slack workspace connected to Omni.
                          example: slack
                        groupDm:
                          $ref: '#/components/schemas/ScheduleSlackGroupDm'
                        recipients:
                          anyOf:
                            - type: string
                              minLength: 1
                            - type: array
                              items:
                                type: string
                                minLength: 1
                              minItems: 1
                          description: |
                            Slack recipient IDs. Accepts a comma-separated string or an array.

                            For a channel, provide exactly one channel ID (e.g., "C01234567" or ["C01234567"]).

                            For users, provide one or more user IDs (e.g., "U111,U222" or ["U111", "U222"]).
                          example: C01234567
                        slackRecipientType:
                          $ref: '#/components/schemas/ScheduleSlackRecipientType'
                        textBody:
                          type: string
                          description: Optional message text to include with the Slack delivery.
                          example: Here is your weekly report!
                      required:
                        - format
                        - destinationType
                        - recipients
                        - slackRecipientType
                    - title: S3
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleExpandTablesToShowAllRows'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        testNow:
                          type: boolean
                          default: false
                          description: 'If true, run the task immediately instead of scheduling. Not supported for `RESULTS_CHANGED` and `RESULTS_UNCHANGED` alert conditions, or for `s3` destinations: create the schedule first, then use the [trigger endpoint](/api/schedules/trigger-schedule).'
                          example: false
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        bucketName:
                          $ref: '#/components/schemas/ScheduleS3BucketName'
                        destinationType:
                          type: string
                          enum:
                            - s3
                          description: The destination type for delivering to an S3 bucket.
                          example: s3
                        filename:
                          $ref: '#/components/schemas/ScheduleS3Filename'
                        keyPrefix:
                          $ref: '#/components/schemas/ScheduleS3KeyPrefix'
                        region:
                          $ref: '#/components/schemas/ScheduleS3Region'
                        roleArn:
                          $ref: '#/components/schemas/ScheduleS3RoleArn'
                      required:
                        - format
                        - bucketName
                        - destinationType
                        - region
                        - roleArn
            examples:
              'Webhook':
                summary: Webhook destination
                value:
                  identifier: "12db1a0a"
                  name: "My Webhook schedule"
                  schedule: "0 9 ? * * *"
                  timezone: "UTC"
                  format: "json"
                  destinationType: "webhook"
                  url: "https://webhooksrus.com/1234566"
              'Workbook tab':
                summary: Workbook tab delivery
                value:
                  identifier: "12db1a0a"
                  entity: "workbook"
                  name: "My Workbook schedule"
                  schedule: "0 9 ? * * *"
                  timezone: "UTC"
                  format: "xlsx"
                  destinationType: "email"
                  queryIdentifierMapKey: "Jmn2r3KV"
                  recipients:
                    - "user1@example.com"
                  subject: "Weekly Workbook Export"
              'Email':
                summary: Email destination
                value:
                  identifier: "12db1a0a"
                  name: "My Email schedule"
                  schedule: "0 9 ? * * *"
                  timezone: "UTC"
                  format: "pdf"
                  destinationType: "email"
                  paperFormat: "legal"
                  expandTablesToShowAllRows: true
                  expandAiSummaries: true
                  paperOrientation: "landscape"
                  recipients:
                    - "user1@example.com"
                    - "user2@example.com"
                  subject: "Daily Sales report"
                  textBody: "Here are the daily sales!"
                  fanOut: false
              'SFTP':
                summary: SFTP destination
                value:
                  identifier: "12db1a0a"
                  name: "My SFTP schedule"
                  schedule: "0 9 ? * * *"
                  timezone: "UTC"
                  format: "xlsx"
                  destinationType: "sftp"
                  address: "sftp.example.com"
                  port: 22
                  username: "sftpuser"
                  passwordUnencrypted: "password123"
              'SFTP with SSH key':
                summary: SFTP destination authenticated with an SSH key
                value:
                  identifier: "12db1a0a"
                  name: "My SFTP schedule"
                  schedule: "0 9 ? * * *"
                  timezone: "UTC"
                  format: "xlsx"
                  destinationType: "sftp"
                  address: "sftp.example.com"
                  port: 22
                  username: "sftpuser"
                  privateKeyUnencrypted: "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----"
              'SFTP with encrypted SSH key':
                summary: SFTP destination authenticated with a passphrase-protected SSH key
                value:
                  identifier: "12db1a0a"
                  name: "My SFTP schedule"
                  schedule: "0 9 ? * * *"
                  timezone: "UTC"
                  format: "xlsx"
                  destinationType: "sftp"
                  address: "sftp.example.com"
                  port: 22
                  username: "sftpuser"
                  privateKeyUnencrypted: "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----"
                  privateKeyPassphraseUnencrypted: "my-key-passphrase"
              'Slack channel':
                summary: Slack channel destination
                value:
                  identifier: "12db1a0a"
                  name: "Weekly Report"
                  schedule: "0 9 ? * MON *"
                  timezone: "America/New_York"
                  format: "pdf"
                  destinationType: "slack"
                  recipients: "C01234567"
                  slackRecipientType: "channel"
                  textBody: "Here is your weekly report!"
              'Slack users':
                summary: Slack users destination
                value:
                  identifier: "12db1a0a"
                  name: "Sales Team Report"
                  schedule: "0 9 ? * * *"
                  timezone: "America/New_York"
                  format: "pdf"
                  destinationType: "slack"
                  recipients:
                    - "U11111111"
                    - "U22222222"
                  slackRecipientType: "users"
                  textBody: "Your daily sales report"
              'Slack users with group direct message':
                summary: Slack users destination, delivered as one group direct message
                value:
                  identifier: "12db1a0a"
                  name: "Sales Team Report"
                  schedule: "0 9 ? * * *"
                  timezone: "America/New_York"
                  format: "pdf"
                  destinationType: "slack"
                  recipients:
                    - "U11111111"
                    - "U22222222"
                  slackRecipientType: "users"
                  groupDm: true
                  textBody: "Your daily sales report"
              'Amazon S3':
                summary: Amazon S3 destination
                value:
                  identifier: "12db1a0a"
                  name: "Daily Export to S3"
                  schedule: "0 9 ? * * *"
                  timezone: "UTC"
                  format: "csv"
                  destinationType: "s3"
                  bucketName: "my-reports-bucket"
                  region: "us-east-1"
                  roleArn: "arn:aws:iam::123456789012:role/OmniS3DeliveryRole"
                  keyPrefix: "reports/daily/"
                  filename: "{{entityName}}-{{currentDate}}"
              'Filters and formatting':
                summary: Filters and formatting
                value:
                  identifier: "dashboard-abc123"
                  name: "Weekly Regional Report"
                  schedule: "0 0 ? * MON *"
                  timezone: "UTC"
                  format: "pdf"
                  destinationType: "email"
                  recipients:
                    - "iamagoodblob@blobsrus.com"
                    - "managerblob@blobsrus.com"
                  subject: "Weekly Regional Report"
                  filterConfig:
                    region:
                      kind: "EQ"
                      left_side: "US"
                      type: "string"
                  hideTitle: true
              'Alert condition':
                summary: Alert condition
                value:
                  identifier: "dashboard-abc123"
                  name: "Data Alert Webhook"
                  schedule: "0 */6 * * ? *"
                  timezone: "UTC"
                  format: "json"
                  destinationType: "webhook"
                  url: "https://api.example.com/webhook"
                  conditionType: "RESULTS_PRESENT"
                  conditionQueryMapKey: "1"
                  queryIdentifierMapKey: "1"
                  overrideRowLimit: true
                  maxRowLimit: 1000
              'Test delivery':
                summary: Test delivery
                value:
                  identifier: "dashboard-abc123"
                  name: "Test Delivery"
                  schedule: "0 0 1 1 ? 2099"
                  timezone: "UTC"
                  format: "pdf"
                  destinationType: "email"
                  recipients:
                    - "iamagoodblob@blobsrus.com"
                  subject: "Test Delivery"
                  testNow: true
      responses:
        '200':
          description: Schedule created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: The ID of the created schedule.
                  message:
                    type: string
                    example: "Successfully created schedule"
                    description: A success message.
                  delivererRoleArn:
                    type: string
                    description: |
                      **S3 destinations only.** The ARN of Omni's deliverer IAM role. The customer uses this as the `Principal` in their IAM trust policy to allow Omni to assume their role.
                  externalId:
                    type: string
                    description: |
                      **S3 destinations only.** The organization ID, used as the external ID in the IAM trust policy `sts:ExternalId` condition. This value is the same for all S3 destinations in the organization.
              examples:
                'Default':
                  summary: Default response
                  value:
                    id: "12db1a0a"
                    message: "Successfully created schedule"
                'Amazon S3':
                  summary: Amazon S3 destination
                  value:
                    id: "12db1a0a"
                    message: "Successfully created schedule"
                    delivererRoleArn: "arn:aws:iam::123456789012:role/OmniS3DeliveryRole"
                    externalId: "<externalId>"
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Analyses do not support dashboards.`
            - `Document does not have a dashboard`
            - `schedule: Schedule is an invalid cron expression`
            - `timezone: Time zone must be IANA valid.`
            - `hideTitle can only be used with PDF or PNG formats`
            - `Invalid filter keys found in schedule configuration: <filterKey>. Available dashboard filter keys are: <key1>, <key2>, ....`
            - `Invalid page found in containerPages: <page>. Available pages are: <id> (<name>), ...`
            - `PNG format supports only one page`
            - `containerPages can only be used with PDF, PNG, CSV or Excel formats` — also returned when `containerPages` is used on a dashboard that doesn't use the advanced (multi-page) layout
            - `Test delivery not supported for condition type RESULTS_CHANGED`
            - `Print options are not supported for this format`
            - `Single column layout is not supported for FIT_PAGE format.`
            - `Must provide both a trigger query and an alert condition type`
            - `slackRecipientType is required for Slack destinations`
            - `Only one Slack channel is supported per schedule`
            - `A Slack group direct message can include at most 8 users`
            - `Organization does not have a Slack credential configured`
            - `Test delivery not supported for S3 destinations`
            - `Personalized deliveries aren't supported on connections that use OAuth sign-in. Edit this delivery to turn off personalization.` — returned for `testNow: true` with `fanOut: true` on a connection that uses OAuth sign-in
            - `Invalid bucket name`
            - `Invalid role ARN`
            - `Invalid region`
            - `Key prefix contains invalid path traversal`
            - `The webhook URL must be an https address on a public host`
            - `The SFTP address must be a public host`
            - `Provide passwordUnencrypted or privateKeyUnencrypted, not both`
            - `privateKeyPassphraseUnencrypted requires privateKeyUnencrypted`
            - `A workbook delivery sends one tab`
            - `Workbook deliveries have no filters`
            - `Workbook deliveries have no single-column layout`
            - `Invalid query identifier provided`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Personal access tokens cannot create schedules on behalf of other users`
            - `You do not have permission to schedule this dashboard`
            - `You do not have permission to schedule this workbook`
            - `Target user does not have permission to view this workbook`
            - `Target user does not have permission to schedule this dashboard`
            - `Target user does not have permission to schedule this workbook`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Document with identifier "<documentId>" not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '409':
          description: |
            `groupDm: true` was requested, but the organization's Slack app doesn't have the `mpim:write` scope.

            Possible error messages:

            - `Sending one group direct message needs the "mpim:write" Slack scope. Reconnect Slack to grant it.`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
    get:
      tags:
        - Schedules
      summary: List schedules
      description: |
        Retrieves scheduled tasks. This endpoint supports filtering, sorting, and cursor-based pagination.

        To retrieve the recipients for a schedule, use the [List schedule recipients](/api/schedule-recipients/list-schedule-recipients) endpoint.
      security:
        - bearerAuth: []
      operationId: schedulesList
      parameters:
        - name: q
          in: query
          schema:
            type: string
          description: |
            Search term for filtering schedules by name, dashboard name, or owner name (case-insensitive).
        - name: status
          in: query
          schema:
            type: string
            enum: [success, error, canceled, none]
          description: Filter schedules by delivery status.
        - name: destination
          in: query
          schema:
            type: string
            enum: [email, slack, webhook, sftp, s3]
          description: Filter schedules by destination type.
        - name: scheduleType
          in: query
          schema:
            type: string
            enum: [alert, schedule]
          description: Filter by schedule type.
        - name: contentType
          in: query
          schema:
            type: string
            enum: [dashboard, single tile, workbook tab]
          description: 'Filter schedules by content type: dashboard, single tile, workbook tab.'
        - name: sortField
          in: query
          schema:
            type: string
            default: scheduleName
            enum: [scheduleName, dashboardName, documentName, ownerName, lastRun, lastRunStatus]
          description: |
            The field to sort results by. `dashboardName` is deprecated; use `documentName` instead.
        - $ref: '#/components/parameters/sortDirection'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/pageSize'
        - name: ownerId
          in: query
          schema:
            type: string
            format: uuid
          description: |
            Filter schedules by the owner's membership ID. Use the [List users](/api/users/list-users) endpoint to retrieve membership IDs.

            API results vary by API key type:

            - **Organization API keys** - When specified, only schedules for the specified owner are returned. Otherwise, all schedules in the organization are returned.
            - **Personal access token** - When specified, only the token owner's membership ID is allowed. If another user's membership ID is provided, the API will return a `403 Forbidden` error.
        - name: embedEntity
          in: query
          schema:
            type: string
          description: |
            Filter schedules by embed entity:

            - If used as the only filter, list all schedules in the embed entity
            - If used with `ownerId`, list only schedules for the specified user if they are associated with the `embedEntity`. The response will be empty if the specified `ownerId` doesn't have the entity.
            - Can be combined with other filters for more specific results
        - name: identifier
          in: query
          schema:
            type: string
          description: |
            Filter schedules by the document's unique identifier. This identifier is shared by a document's dashboard and workbook. This can be retrieved by:

            - **Opening the document settings** - Navigate to **File > Document settings** in the dashboard and then click **Settings**. The **Identifier** field contains the document's ID.
            - **Using the dashboard's URL** - The string after `/dashboards` is the document's ID; for example: `https://myorg.omniapp.co/dashboards/12db1a0a`
      responses:
        '200':
          description: Paginated schedule list
          content:
            application/json:
              schema:
                type: object
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    description: List of schedule records.
                    items:
                      type: object
                      properties:
                        alert:
                          $ref: '#/components/schemas/ScheduleAlertObject'
                        content:
                          $ref: '#/components/schemas/ScheduleContent'
                        dashboardName:
                          $ref: '#/components/schemas/DashboardName'
                        destinationType:
                          $ref: '#/components/schemas/ScheduleDestinationType'
                        disabledAt:
                          nullable: true
                          $ref: '#/components/schemas/ScheduleDisabledAt'
                        documentName:
                          $ref: '#/components/schemas/DocumentName'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        id:
                          $ref: '#/components/schemas/ScheduleId'
                        identifier:
                          $ref: '#/components/schemas/DashboardIdentifier'
                        lastCompletedAt:
                          $ref: '#/components/schemas/ScheduleLastCompletedAt'
                        lastStatus:
                          $ref: '#/components/schemas/ScheduleLastStatus'
                        name:
                          $ref: '#/components/schemas/ScheduleName'
                        ownerId:
                          $ref: '#/components/schemas/ScheduleOwnerId'
                        ownerName:
                          $ref: '#/components/schemas/ScheduleOwner'
                        recipientCount:
                          type: number
                          description: Number of recipients (-1 for non-email destinations)
                          example: 5
                        schedule:
                          $ref: '#/components/schemas/ScheduleCron'
                        slackRecipientType:
                          nullable: true
                          $ref: '#/components/schemas/ScheduleSlackRecipientType'
                        systemDisabledAt:
                          nullable: true
                          $ref: '#/components/schemas/ScheduleSystemDisabledAt'
                        systemDisabledReason:
                          nullable: true
                          $ref: '#/components/schemas/ScheduleSystemDisabledReason'
                        timezone:
                          $ref: '#/components/schemas/ScheduleTimezone'
                      required:
                        - content
                        - dashboardName
                        - destinationType
                        - disabledAt
                        - documentName
                        - format
                        - id
                        - identifier
                        - lastCompletedAt
                        - lastStatus
                        - name
                        - ownerId
                        - ownerName
                        - recipientCount
                        - schedule
                        - slackRecipientType
                        - systemDisabledAt
                        - systemDisabledReason
                        - timezone             
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Invalid enum value`
            - `ownerId: Invalid uuid`
            - `Page size cannot exceed 100`
            - `Invalid page number. The last valid page is <number>.`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `Personal access tokens can only filter by their own ownerId`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `User with id <id> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/schedules/{scheduleId}:
    get:
      tags:
        - Schedules
      summary: Get schedule
      description: |
        Retrieve the details of a single schedule, including its delivery destinations, recurrence configuration, and current status. A schedule can deliver a dashboard or a workbook.
      x-mint:
        content: |
          ## API key behavior

          How this endpoint behaves depends on the type of API key being used:

          | API Key Type          | Behavior                                                                                                        |
          | --------------------- | --------------------------------------------------------------------------------------------------------------- |
          | Personal Access Token | The user must be able to view and manage the associated dashboard or workbook. Users who can view and schedule but do not have permissions to manage schedules must also be the schedule owner to access the schedule. |
          | Organization API Key  | Acts at org level with no additional permission check; optionally accepts `userId` to check permissions as a specific user. |
      security:
        - bearerAuth: []
      operationId: schedulesGet
      parameters:
        - $ref: '#/components/parameters/scheduleId'
        - name: userId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: |
            Membership ID of the user whose permissions should be checked (Organization API keys only). When provided, the endpoint verifies the user can view and schedule the associated dashboard or workbook before returning the schedule.
      responses:
        '200':
          description: Schedule retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: Schedule UUID
                  name:
                    type: string
                    description: Schedule name
                  organizationId:
                    type: string
                    format: uuid
                    description: Organization UUID
                  entity:
                    type: string
                    enum: [dashboard, workbook]
                    description: |
                      What the schedule delivers: the dashboard, or a tab of the workbook.
                  documentIdentifier:
                    type: string
                    description: Identifier of the document the schedule delivers. Shared by the document's dashboard and workbook.
                  entityId:
                    type: string
                    format: uuid
                    deprecated: true
                    description: |
                      Deprecated. Use `documentIdentifier` instead. Internal ID of the dashboard, or of the document for a workbook delivery. For dashboards, this value changes when the dashboard is published.
                  owner:
                    $ref: '#/components/schemas/ScheduleOwner'
                  ownerId:
                    $ref: '#/components/schemas/ScheduleOwnerId'
                  schedule:
                    $ref: '#/components/schemas/ScheduleCron'
                  timezone:
                    $ref: '#/components/schemas/ScheduleTimezone'
                  fanOut:
                    type: boolean
                    description: |
                      When `true`, each recipient receives a personalized delivery. The value returned reflects the organization's [delivery personalization setting](/administration/content-permissions#delivery-personalization): if set to **Never**, this will always be `false`; if set to **Always**, this reflects the forced-personalization state. See [Personalizing email deliveries](/share/deliveries/email/personalization) for more information.

                      **Note**: Personalized deliveries aren't supported for connections that use OAuth sign-in. In this case, the delivery will fail.
                  killJobsOnFailure:
                    $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                  filterConfig:
                    $ref: '#/components/schemas/ScheduleFilterConfig'
                    nullable: true
                    description: |
                      Dashboard deliveries only. The effective dashboard filter configuration the schedule runs with. Omitted entirely (not `null`) for workbook deliveries, which have no filters.
                  metadata:
                    type: object
                    nullable: true
                    description: Format options and other delivery settings
                  conditionQueryMapKey:
                    $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                    nullable: true
                  conditionType:
                    $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                    nullable: true
                  disabledAt:
                    type: string
                    format: date-time
                    nullable: true
                    description: Timestamp when the schedule was manually paused. `null` if active.
                  systemDisabledAt:
                    nullable: true
                    $ref: '#/components/schemas/ScheduleSystemDisabledAt'
                  systemDisabledReason:
                    nullable: true
                    $ref: '#/components/schemas/ScheduleSystemDisabledReason'
                  createdAt:
                    type: string
                    format: date-time
                    description: ISO 8601 creation timestamp
                  updatedAt:
                    type: string
                    format: date-time
                    description: ISO 8601 last-updated timestamp
                  destinations:
                    type: array
                    description: Delivery destination configurations
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: Destination UUID
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        lastStatus:
                          type: string
                          nullable: true
                          enum: [COMPLETE, ERROR, ERROR_DELIVERED, KILLED, CONDITION_UNMET]
                          description: Status of last delivery
                        lastCompletedAt:
                          type: string
                          format: date-time
                          nullable: true
                          description: ISO 8601 timestamp of last successful delivery
                        metadata:
                          type: object
                          nullable: true
                          description: Destination-specific configuration
                        recipients:
                          type: array
                          items:
                            type: object
                            properties:
                              id:
                                type: string
                                format: uuid
                              membershipId:
                                type: string
                                format: uuid
                              membership:
                                type: object
                                properties:
                                  user:
                                    type: object
                                    properties:
                                      email:
                                        type: string
                                        format: email
                                      name:
                                        type: string
                                        nullable: true
                        userGroupRecipients:
                          type: array
                          items:
                            type: object
                required:
                  - conditionQueryMapKey
                  - conditionType
                  - createdAt
                  - destinations
                  - disabledAt
                  - documentIdentifier
                  - entity
                  - entityId
                  - fanOut
                  - id
                  - killJobsOnFailure
                  - name
                  - organizationId
                  - owner
                  - ownerId
                  - schedule
                  - systemDisabledAt
                  - systemDisabledReason
                  - timezone
                  - updatedAt
              examples:
                dashboardDelivery:
                  summary: Dashboard delivery
                  value:
                    id: "123e4567-e89b-12d3-a456-426614174000"
                    name: "Weekly Sales Report"
                    organizationId: "org-uuid"
                    entity: "dashboard"
                    documentIdentifier: "12db1a0a"
                    entityId: "dashboard-uuid"
                    ownerId: "user-uuid"
                    owner:
                      name: "Jane Smith"
                    schedule: "0 9 ? * MON *"
                    timezone: "America/New_York"
                    fanOut: false
                    killJobsOnFailure: false
                    filterConfig: null
                    metadata: null
                    conditionType: null
                    conditionQueryMapKey: null
                    disabledAt: null
                    systemDisabledAt: null
                    systemDisabledReason: null
                    createdAt: "2025-01-15T10:00:00.000Z"
                    updatedAt: "2025-01-20T08:30:00.000Z"
                    destinations:
                      - id: "dest-uuid"
                        format: "pdf"
                        lastStatus: "COMPLETE"
                        lastCompletedAt: "2025-01-20T09:00:05.000Z"
                        metadata:
                          type: "email"
                        recipients:
                          - id: "recipient-uuid"
                            membershipId: "membership-uuid"
                            membership:
                              user:
                                email: "alice@example.com"
                                name: "Alice"
                        userGroupRecipients: []
                workbookDelivery:
                  summary: Workbook delivery
                  value:
                    id: "223e4567-e89b-12d3-a456-426614174001"
                    name: "Weekly Workbook Export"
                    organizationId: "org-uuid"
                    entity: "workbook"
                    documentIdentifier: "12db1a0a"
                    entityId: "document-uuid"
                    ownerId: "user-uuid"
                    owner:
                      name: "Jane Smith"
                    schedule: "0 9 ? * MON *"
                    timezone: "America/New_York"
                    fanOut: false
                    killJobsOnFailure: false
                    metadata: null
                    conditionType: null
                    conditionQueryMapKey: null
                    disabledAt: null
                    systemDisabledAt: null
                    systemDisabledReason: null
                    createdAt: "2025-01-15T10:00:00.000Z"
                    updatedAt: "2025-01-20T08:30:00.000Z"
                    destinations:
                      - id: "dest-uuid"
                        format: "xlsx"
                        lastStatus: "COMPLETE"
                        lastCompletedAt: "2025-01-20T09:00:05.000Z"
                        metadata:
                          type: "email"
                        recipients:
                          - id: "recipient-uuid"
                            membershipId: "membership-uuid"
                            membership:
                              user:
                                email: "alice@example.com"
                                name: "Alice"
                        userGroupRecipients: []
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `scheduleId` is not a valid UUID
            - `userId` is not a valid UUID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              examples:
                invalidScheduleId:
                  summary: Invalid schedule ID
                  value:
                    detail: "Bad Request: scheduleId: Invalid UUID"
                    status: 400
                invalidUserId:
                  summary: Invalid user ID
                  value:
                    detail: "Bad Request: userId: Invalid UUID"
                    status: 400
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Forbidden

            The authenticated user does not have permission to view the schedule. Possible scenarios:

            - User cannot view the associated dashboard or workbook
            - User cannot schedule the associated dashboard or workbook
            - User is not the schedule owner
            - User-scoped API key attempted to use `userId` parameter
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              examples:
                cannotViewDashboard:
                  summary: Cannot view dashboard
                  value:
                    detail: "You do not have permission to view this dashboard"
                    status: 403
                cannotViewWorkbook:
                  summary: Cannot view workbook
                  value:
                    detail: "You do not have permission to view this workbook"
                    status: 403
                cannotScheduleDashboard:
                  summary: Cannot schedule dashboard
                  value:
                    detail: "User does not have permission to schedule this dashboard"
                    status: 403
                cannotScheduleWorkbook:
                  summary: Cannot schedule workbook
                  value:
                    detail: "User does not have permission to schedule this workbook"
                    status: 403
                userScopedKeyWithUserId:
                  summary: User-scoped key cannot act as another user
                  value:
                    detail: "User-scoped API keys cannot act on behalf of other users"
                    status: 403
        '404':
          description: |
            Not Found

            Possible error messages:

            - Schedule not found (or belongs to a different organization)
            - `userId` not found in the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              examples:
                scheduleNotFound:
                  summary: Schedule not found
                  value:
                    detail: "Scheduled task with id 123e4567-e89b-12d3-a456-426614174000 does not exist"
                    status: 404
                userNotFound:
                  summary: User not found
                  value:
                    detail: "User with id foreign-membership-uuid does not exist"
                    status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - Schedules
      summary: Update schedule
      description: |
        Update the specified task, whether it delivers a dashboard or a tab of a workbook. Changes to the schedule are applied to future runs. Currently running jobs are not affected.

        <Note>
          A schedule's `entity` (dashboard or workbook) is set when the schedule is created and can't change. If the request body includes an `entity` value, it has no effect.
        </Note>

        <Warning>
          This endpoint replaces the schedule's entire configuration — it is not a partial update. The request must include every required field for the destination type. Any optional property left out of the request is reset to its default, not left at its current value. This includes `filterConfig` (filter values are cleared), `fanOut` (treated as `false`, unless your organization's [delivery personalization setting](/administration/content-permissions#delivery-personalization) is set to **Always**), `conditionType` and `conditionQueryMapKey` (the alert condition is removed), `queryIdentifierMapKey`, `containerPages` (selected pages are cleared), `maxRowLimit`, `textBody`, `filename`, and the paper and layout options.

          The one exception is `timezoneOverride`: it keeps its current value when omitted, but only if query timezone overrides are enabled for the connection or document. Otherwise, it's cleared.

          To change a single property, send the schedule's full configuration with that property changed.
        </Warning>
      security:
        - bearerAuth: []
      operationId: updateSchedule
      parameters:
        - $ref: '#/components/parameters/scheduleId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - type: object
                  properties:
                    name:
                      type: string
                      minLength: 1
                      description: The name of the scheduled task. Must be at least 1 character.
                      example: Weekly Sales Report
                    schedule:
                      type: string
                      description: 'When the task runs, as an AWS EventBridge cron expression: minute hour day-of-month month day-of-week year. Runs cannot be more frequent than once an hour unless the organization allows a higher frequency. Delivery times are approximate and happen within 15 minutes of the scheduled time.'
                      example: 0 9 ? * MON *
                    timezone:
                      type: string
                      description: The IANA timezone for interpreting the schedule. Must be a valid IANA timezone identifier.
                      example: America/New_York
                    timezoneOverride:
                      type: string
                      nullable: true
                      description: Optional IANA timezone applied to query execution at render time. Distinct from `timezone` (which controls *when* the schedule fires). Pass `null` to clear it (use the default). Omitting it keeps the current value only while query timezone overrides are enabled for the connection or document; otherwise it is cleared.
                      example: Europe/Paris
                  required:
                    - name
                    - schedule
                    - timezone
                - oneOf:
                    - title: Email
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        destinationType:
                          type: string
                          enum:
                            - email
                          description: The destination type for delivering to email recipients.
                          example: email
                        fanOut:
                          $ref: '#/components/schemas/ScheduleEmailFanOut'
                        filename:
                          type: string
                          description: Optional custom filename for the attachment (without extension). Must not contain special characters.
                          example: weekly-sales-report
                        recipients:
                          description: Email recipients. Can be email addresses or user group IDs (if allowed).
                          anyOf:
                            - type: string
                              minLength: 1
                            - type: array
                              items:
                                type: string
                              minItems: 1
                            - type: object
                              properties:
                                emails:
                                  type: array
                                  items:
                                    type: string
                                  default: []
                                userGroupIds:
                                  type: array
                                  items:
                                    type: string
                                    format: uuid
                                  default: []
                        subject:
                          type: string
                          minLength: 1
                          description: The subject line of the email. Must be at least 1 character.
                          example: Weekly Sales Report
                        textBody:
                          type: string
                          description: Optional plain text body content for the email.
                          example: Please find attached the weekly sales report.
                      required:
                        - format
                        - destinationType
                        - recipients
                        - subject
                    - title: Webhook
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                          example: Jmn2r3KV
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        destinationType:
                          type: string
                          enum:
                            - webhook
                          description: The destination type for delivering to a webhook URL.
                          example: webhook
                        filename:
                          type: string
                          description: Optional custom filename for the webhook payload.
                          example: report-data
                        url:
                          type: string
                          description: 'The webhook URL to deliver the content to. Must be a public HTTPS URL: IP addresses in private, loopback, link-local or reserved ranges, and `.internal`, `.local` or `.localhost` hosts, are rejected.'
                          example: https://api.example.com/webhooks/reports
                      required:
                        - format
                        - destinationType
                        - url
                    - title: SFTP
                      description: |
                        Provide `credentialId`, `passwordUnencrypted`, or `privateKeyUnencrypted`. If all properties are omitted, the update removes the stored credential.
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        address:
                          type: string
                          description: 'The SFTP server address including the path. Format: sftp://hostname/path/to/directory. Must be a public host: IP addresses in private, loopback, link-local or reserved ranges, and `.internal`, `.local` or `.localhost` hosts, are rejected.'
                          example: sftp://sftp.example.com/reports/weekly
                        credentialId:
                          type: string
                          description: |
                            **Required if neither `passwordUnencrypted` nor `privateKeyUnencrypted` is provided.** The ID of a stored credential to use for SFTP authentication.
                          example: sftp-cred-123
                        destinationType:
                          type: string
                          enum:
                            - sftp
                          description: The destination type for delivering to an SFTP server.
                          example: sftp
                        filename:
                          type: string
                          description: Optional custom filename for the uploaded file (without extension).
                          example: weekly-report
                        passwordUnencrypted:
                          type: string
                          minLength: 1
                          description: |
                            **Required if `credentialId` is not provided.** The password for SFTP authentication. Can't be combined with `privateKeyUnencrypted`.
                          example: secretpassword
                        port:
                          type: number
                          minimum: 1
                          maximum: 65535
                          description: The SFTP server port. Standard SFTP port is 22.
                          example: 22
                        privateKeyPassphraseUnencrypted:
                          type: string
                          description: |
                            **Required if `privateKeyUnencrypted` is provided**. The passphrase for `privateKeyUnencrypted`, if the key is encrypted.
                        privateKeyUnencrypted:
                          type: string
                          minLength: 1
                          description: |
                            **Required if neither `credentialId` nor `passwordUnencrypted` is provided.** The SSH private key for SFTP authentication, in one of the following formats:
                            
                            - **OpenSSH** - `-----BEGIN OPENSSH PRIVATE KEY-----`
                            - **PEM RSA/EC format** - `-----BEGIN RSA PRIVATE KEY-----`, `-----BEGIN EC PRIVATE KEY-----`). 
                            
                            PKCS#8 keys (`-----BEGIN PRIVATE KEY-----`) aren't supported. 
                            
                            Can't be combined with `passwordUnencrypted`.
                          example: |-
                            -----BEGIN OPENSSH PRIVATE KEY-----
                            ...
                            -----END OPENSSH PRIVATE KEY-----
                        username:
                          type: string
                          minLength: 1
                          description: The username for SFTP authentication.
                          example: sftpuser
                      required:
                        - format
                        - address
                        - destinationType
                        - port
                        - username
                    - title: Slack
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        destinationType:
                          type: string
                          enum:
                            - slack
                          description: The destination type for delivering to Slack. The organization must have a Slack workspace connected to Omni.
                          example: slack
                        groupDm:
                          $ref: '#/components/schemas/ScheduleSlackGroupDm'
                          description: |
                            **Slack users only.** If `true`, delivers each run as a single group direct message to everyone listed, instead of a separate direct message to each recipient. Requires the Slack app to hold the `mpim:write` scope. Allows at most 8 users.

                            An update replaces the stored value, so include it any time you change other Slack destination fields.
                        recipients:
                          anyOf:
                            - type: string
                              minLength: 1
                            - type: array
                              items:
                                type: string
                                minLength: 1
                              minItems: 1
                          description: |
                            Slack recipient IDs. Accepts a comma-separated string or an array.

                            For a channel, provide exactly one channel ID (e.g., "C01234567" or ["C01234567"]).

                            For users, provide one or more user IDs (e.g., "U111,U222" or ["U111", "U222"]).
                          example: C01234567
                        slackRecipientType:
                          type: string
                          enum:
                            - channel
                            - users
                          description: The type of Slack recipient. Use `channel` to deliver to a single Slack channel, or `users` to deliver to one or more Slack users via direct message.
                          example: channel
                        textBody:
                          type: string
                          description: Optional message text to include with the Slack delivery.
                          example: Here is your weekly report!
                      required:
                        - format
                        - destinationType
                        - recipients
                        - slackRecipientType
                    - title: S3
                      type: object
                      properties:
                        conditionQueryMapKey:
                          $ref: '#/components/schemas/ScheduleConditionQueryMapKey'
                        conditionType:
                          $ref: '#/components/schemas/ScheduleConditionType'
                        enableConditionalFormatting:
                          $ref: '#/components/schemas/ScheduleEnableConditionalFormatting'
                        enableFormatting:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandTablesToShowAllRows:
                          $ref: '#/components/schemas/ScheduleEnableFormatting'
                        expandAiSummaries:
                          $ref: '#/components/schemas/ExpandAiSummaries'
                        filterConfig:
                          $ref: '#/components/schemas/ScheduleFilterConfig'
                        format:
                          $ref: '#/components/schemas/ScheduleFormat'
                        hideHiddenFields:
                          $ref: '#/components/schemas/ScheduleHiddenFields'
                        hideTitle:
                          $ref: '#/components/schemas/ScheduleHideTitle'
                        killJobsOnFailure:
                          $ref: '#/components/schemas/ScheduleKillJobsOnFailure'
                        maxRowLimit:
                          $ref: '#/components/schemas/ScheduleMaxRowLimit'
                        overrideRowLimit:
                          $ref: '#/components/schemas/ScheduleOverrideRowLimit'
                        paperFormat:
                          $ref: '#/components/schemas/SchedulePaperFormat'
                        paperOrientation:
                          $ref: '#/components/schemas/SchedulePaperOrientation'
                        queryIdentifierMapKey:
                          $ref: '#/components/schemas/ScheduleQueryIdentifierMapKey'
                        containerPages:
                          $ref: '#/components/schemas/ScheduleContainerPages'
                        rebuildExtracts:
                          $ref: '#/components/schemas/ScheduleRebuildExtracts'
                        showContentLink:
                          $ref: '#/components/schemas/ScheduleShowContentLink'
                        showFilters:
                          $ref: '#/components/schemas/ScheduleShowFilters'
                        singleColumnLayout:
                          $ref: '#/components/schemas/ScheduleSingleColumnLayout'
                        useCache:
                          $ref: '#/components/schemas/ScheduleUseCache'
                        bucketName:
                          type: string
                          description: The S3 bucket name. Must be 3-63 characters, lowercase letters, numbers, hyphens, and dots only.
                          example: my-reports-bucket
                        destinationType:
                          type: string
                          enum:
                            - s3
                          description: The destination type for delivering to an S3 bucket.
                          example: s3
                        filename:
                          type: string
                          description: 'Optional custom filename for the uploaded file (without extension). Supports Mustache variables: {{currentDate}}, {{currentTime}}, {{currentYear}}, {{currentMonth}}, {{currentMonthName}}, {{currentDay}}, {{yesterdayDate}}, {{tomorrowDate}}, {{previousMonth}}, {{previousMonthName}}, {{previousMonthYear}}, {{timeZone}}, {{entityName}}, {{format}}, {{scheduledTaskName}}.'
                          example: report-{{currentDate}}
                        keyPrefix:
                          type: string
                          description: Optional S3 key prefix (folder path) for the uploaded file. Leading slashes are normalized.
                          example: reports/weekly/
                        region:
                          type: string
                          enum:
                            - us-east-1
                            - us-east-2
                            - us-west-1
                            - us-west-2
                            - eu-west-1
                            - eu-west-2
                            - eu-west-3
                            - eu-central-1
                            - ap-northeast-1
                            - ap-northeast-2
                            - ap-southeast-1
                            - ap-southeast-2
                            - ap-south-1
                            - sa-east-1
                            - ca-central-1
                          description: The AWS region where the S3 bucket is located.
                          example: us-east-1
                        roleArn:
                          type: string
                          description: The ARN of the cross-account IAM role that Omni will assume to write to the S3 bucket.
                          example: arn:aws:iam::123456789012:role/OmniS3DeliveryRole
                      required:
                        - format
                        - bucketName
                        - destinationType
                        - region
                        - roleArn
            examples:
              'Email':
                summary: Email destination
                value:
                  name: "Weekly Sales Report"
                  schedule: "0 9 * * 1"
                  timezone: "America/New_York"
                  format: "pdf"
                  destinationType: "email"
                  recipients:
                    - "user1@example.com"
                  subject: "Weekly Sales Report"
              'SFTP':
                summary: SFTP destination
                value:
                  name: "Nightly Export"
                  schedule: "0 0 * * *"
                  timezone: "UTC"
                  format: "csv"
                  destinationType: "sftp"
                  address: "sftp://sftp.example.com/reports/weekly"
                  port: 22
                  username: "sftpuser"
                  passwordUnencrypted: "secretpassword"
              'SFTP with SSH key':
                summary: SFTP destination authenticated with an SSH key
                value:
                  name: "Nightly Export"
                  schedule: "0 0 * * *"
                  timezone: "UTC"
                  format: "csv"
                  destinationType: "sftp"
                  address: "sftp://sftp.example.com/reports/weekly"
                  port: 22
                  username: "sftpuser"
                  privateKeyUnencrypted: "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----"
              'SFTP with encrypted SSH key':
                summary: SFTP destination authenticated with a passphrase-protected SSH key
                value:
                  name: "Nightly Export"
                  schedule: "0 0 * * *"
                  timezone: "UTC"
                  format: "csv"
                  destinationType: "sftp"
                  address: "sftp://sftp.example.com/reports/weekly"
                  port: 22
                  username: "sftpuser"
                  privateKeyUnencrypted: "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----"
                  privateKeyPassphraseUnencrypted: "my-key-passphrase"
      responses:
        '200':
          description: Schedule updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  scheduledTaskId:
                    type: string
                    format: uuid
                    description: The UUID of the updated scheduled task.
                    example: "123e4567-e89b-12d3-a456-426614174000"
                  success:
                    type: boolean
                    description: Whether the operation was successful.
                    example: true
                required:
                  - scheduledTaskId
                  - success
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: scheduleId: Invalid uuid`
            - `Invalid method`
            - `Bad Request: name: name is required, schedule: schedule is required, timezone: timezone is required, destinationType: Invalid discriminator value. Expected email | webhook | sftp | slack | s3`
            - `schedule: Schedule is an invalid cron expression`
            - `timezone: Time zone must be IANA valid.`
            - `hideTitle can only be used with PDF or PNG formats`
            - `Invalid filter keys found in schedule configuration: <filterKey>. Available dashboard filter keys are: <key1>, <key2>, ....`
            - `Invalid page found in containerPages: <page>. Available pages are: <id> (<name>), ...`
            - `PNG format supports only one page`
            - `containerPages can only be used with PDF, PNG, CSV or Excel formats` — also returned when `containerPages` is used on a dashboard that doesn't use the advanced (multi-page) layout
            - `Print options are not supported for this format`
            - `Single column layout is not supported for FIT_PAGE format.`
            - `slackRecipientType is required for Slack destinations`
            - `Only one Slack channel is supported per schedule`
            - `A Slack group direct message can include at most 8 users`
            - `Organization does not have a Slack credential configured`
            - `Invalid bucket name`
            - `Invalid role ARN`
            - `Invalid region`
            - `Key prefix contains invalid path traversal`
            - `The webhook URL must be an https address on a public host`
            - `The SFTP address must be a public host`
            - `Provide passwordUnencrypted or privateKeyUnencrypted, not both`
            - `privateKeyPassphraseUnencrypted requires privateKeyUnencrypted`
            - `A workbook delivery sends one tab`
            - `Workbook deliveries have no filters`
            - `Invalid query identifier provided`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Scheduled task with id <scheduleId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: |
            `groupDm: true` was requested, but the organization's Slack app doesn't have the `mpim:write` scope.

            Possible error messages:

            - `Sending one group direct message needs the "mpim:write" Slack scope. Reconnect Slack to grant it.`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      tags:
        - Schedules
      summary: Delete schedule
      description: Delete a schedule.
      security:
        - bearerAuth: []
      operationId: deleteSchedule
      parameters:
        - $ref: '#/components/parameters/scheduleId'
      responses:
        '200':
          description: Schedule deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `scheduleId: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Scheduled task with id <scheduleId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/schedules/{scheduleId}/trigger:
    post:
      tags:
        - Schedules
      summary: Trigger schedule
      description: Trigger the execution of a schedule on demand, outside of its regular schedule.
      security:
        - bearerAuth: []
      operationId: triggerSchedule
      parameters:
        - $ref: '#/components/parameters/scheduleId'
      responses:
        '200':
          description: Schedule triggered successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: scheduleId: Invalid uuid`
            - `Invalid method`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Scheduled task with id <scheduleId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          description: |
            409 Conflict

            Possible error messages:

            - `Cannot trigger schedule while another execution is in progress` - Occurs when an `EMAIL` schedule has more than 50 recipients, has `fanOut: true`, and is currently in progress
            - `Cannot trigger an inactive schedule` - Occurs when the schedule is paused (`disabledAt`) or system-disabled (`systemDisabledAt`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          description: |
            Internal Server Error

            Possible error messages:

            - Various error messages related to dispatch failures
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/schedules/{scheduleId}/pause:
    put:
      tags:
        - Schedules
      summary: Pause schedule
      description: Pause a schedule.
      security:
        - bearerAuth: []
      operationId: pauseSchedule
      parameters:
        - $ref: '#/components/parameters/scheduleId'
      responses:
        '200':
          description: Schedule paused successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Schedule is system disabled`
            - `Schedule is already paused`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Scheduled task with id <scheduleId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/schedules/{scheduleId}/resume:
    put:
      tags:
        - Schedules
      summary: Resume schedule
      description: Resume a schedule.
      security:
        - bearerAuth: []
      operationId: resumeSchedule
      parameters:
        - $ref: '#/components/parameters/scheduleId'
      responses:
        '200':
          description: Schedule resumed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Indicates the schedule has been resumed successfully.
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Schedule is system disabled`
            - `Schedule is already resumed`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Scheduled task with id <scheduleId> does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/schedules/{scheduleId}/transfer-ownership:
    put:
      tags:
        - Schedules
      summary: Transfer schedule ownership
      description: Transfer ownership of a schedule from one user to another user in the same organization. New ownership will be immediate and can't be reversed using this endpoint.
      x-mint:
        content: |
          How ownership transfer affects schedule execution depends on the type of destination the schedule uses, and for email destinations, whether the [**Personalize delivery with the recipient's user attributes**](/share/deliveries/email/personalization) for more information. is enabled:

          |                                                                     | Slack, SFTP, &amp; Webhooks | Email without personalization | Email with personalization |
          |---------------------------------------------------------------------|-----------------------------|-------------------------------|----------------------------|
          | **Future jobs are executed by...**                                  | New owner                   | New owner                     | Each recipient             |
          | **Permissions, data access, &amp; user attributes are based on...** | New owner                   | New owner                     | Each recipient             |
          | **Output is generated using...**                                    | New owner access            | New owner access              | Recipient access           |
      security:
        - bearerAuth: []
      operationId: transferScheduleOwnership
      parameters:
        - $ref: '#/components/parameters/scheduleId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
              properties:
                userId:
                  type: string
                  format: uuid
                  description: |
                    The ID of the user to transfer schedule ownership to. Use the [List users](/api/users/list-users) endpoint to retrieve user IDs.

                    The new owner must:

                    - Be a member of the same organization
                    - Not be the current owner
                    - Have permission to view and schedule the document the schedule delivers (dashboard or workbook)
      responses:
        '200':
          description: Ownership transferred successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `userId: Invalid uuid`
            - `userId is a required field`
            - `User already owns the schedule`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Forbidden

            Possible error messages:

            - `New owner does not have permission to view the dashboard` (dashboard delivery)
            - `New owner does not have permission to view this workbook` (workbook delivery)
            - `New owner does not have permission to schedule this dashboard` (dashboard delivery)
            - `New owner does not have permission to schedule this workbook` (workbook delivery)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Scheduled task with id <scheduleId> does not exist`
            - `New owner is not a member of the organization`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/schedules/{scheduleId}/recipients:
    get:
      tags:
        - Schedule recipients
      summary: List schedule recipients
      description: |
        Retrieve the list of recipients for the specified schedule. Successful responses will include the recipients and details about the schedule's destination type.

        To retrieve a schedule's full configuration, use the [List schedules](/api/schedules#list-schedules) endpoint.
      security:
        - bearerAuth: []
      operationId: listScheduleRecipients
      parameters:
        - $ref: '#/components/parameters/scheduleId'
      responses:
        '200':
          description: |
            Successful requests return a `200 OK` status and response body specific to the schedule's destination type.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ScheduleRecipientsEmail'
                  - $ref: '#/components/schemas/ScheduleRecipientsSftp'
                  - $ref: '#/components/schemas/ScheduleRecipientsSlack'
                  - $ref: '#/components/schemas/ScheduleRecipientsWebhook'
                  - $ref: '#/components/schemas/ScheduleRecipientsS3'
              examples:
                email:
                  summary: Email destination
                  value:
                    recipients:
                      - email: "blobby@blobsrus.com"
                        emailOnly: false
                        id: "3e7c4152-cdac-42ce-8749-fe3b81d5d8ab"
                        name: "blobby@blobsrus.com"
                    userGroupRecipients:
                      - id: "X9kQ2mPz"
                        name: "Sales Team"
                        recipients:
                          - email: "hoppy@blobsrus.com"
                            id: "2c6e8a3e-6a68-4d0a-9e3e-5e4e6c8f9a1b"
                            name: "hoppy@blobsrus.com"
                    type: "email"
                sftp:
                  summary: SFTP destination
                  value:
                    type: "sftp"
                    address: "sftp.example.com"
                    port: 22
                    username: "username"
                slackChannel:
                  summary: Slack destination with channel recipients
                  value:
                    type: "slack"
                    recipients:
                      - recipientType: "channel"
                        slackId: "C123456789"
                slackUser:
                  summary: Slack destination with user recipients
                  value:
                    type: "slack"
                    recipients:
                      - recipientType: "users"
                        slackId: "U123456789"
                webhook:
                  summary: Webhook destination
                  value:
                    type: "webhook"
                    url: "https://example.com/webhook"
                s3:
                  summary: Amazon S3 destination
                  value:
                    type: "s3"
                    bucketName: "my-reports-bucket"
                    region: "us-east-1"
                    roleArn: "arn:aws:iam::123456789012:role/OmniS3DeliveryRole"
                    externalId: "4fbeb98a-d973-4529-9f36-0df471894ea8"
                    keyPrefix: "reports/daily/"
                    filename: "{{entityName}}-{{currentDate}}"
        '400':
          description: |
            Bad Request. Possible error messages include:

            - `Bad Request: scheduleId: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '404':
          description: |
            Not Found. Possible error messages include:

            - `Scheduled task with id {scheduleId} does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/schedules/{scheduleId}/add-recipients:
    put:
      tags:
        - Schedule recipients
      summary: Add schedule recipients
      description: |
        Add one or more recipients to an existing scheduled email task. Recipients can be specified by email address, user ID, or user group ID.

        <Note>
          This endpoint only works with schedules that have [email destinations](/share/deliveries/email).
        </Note>
      security:
        - bearerAuth: []
      operationId: addScheduleRecipients
      parameters:
        - $ref: '#/components/parameters/scheduleId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                emails:
                  type: array
                  items:
                    type: string
                    format: email
                  description: |
                    **At least one email, user ID, or user group ID must be provided.** Array of email addresses to add as recipients.
                userGroupIds:
                  type: array
                  items:
                    type: string
                  description: |
                    **At least one email, user ID, or user group ID must be provided.** Array of user group IDs to add as recipients. Use the [List user groups](/api/user-groups/list-user-groups) endpoint to retrieve user group IDs.
                  example: ["X9kQ2mPz"]
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: |
                    **At least one email, user ID, or user group ID must be provided.** Array of user UUIDs to add as recipients. Use the [List users](/api/users#list-users) and [List embed users](/api/users#list-embed-users) endpoints to retrieve user IDs.
      responses:
        '200':
          description: Recipients added successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  addedRecipientsCount:
                    type: integer
                    description: The number of recipients added to the schedule.
                  success:
                    type: boolean
                    description: Indicates whether the operation was successful.
              example:
                addedRecipientsCount: 2
                success: true
        '400':
          description: |
            Bad Request. Possible error messages include:

            - `userIds: Invalid uuid`
            - `userGroupIds: Invalid user group ID` - returned for an unknown ID, a full user group UUID, or an embed user group's ID
            - `emails: Invalid email address`
            - `Please provide either valid email addresses, valid user IDs, or both`
            - `At least one recipient must be provided`
            - `{parameter}: Array must contain at least 1 element(s)`
            - `Invalid recipient(s): The following members do not exist or do not have access to this organization: {userId}`
            - `Cannot add recipients to destination type {destinationType}`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '404':
          description: |
            Not Found. Possible error messages include:

            - `Scheduled task with id {scheduleId} does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/schedules/{scheduleId}/remove-recipients:
    put:
      tags:
        - Schedule recipients
      summary: Remove schedule recipients
      description: |
        Remove one or more recipients from an existing scheduled email task. Recipients can be specified by email address, user ID, or user group ID.

        <Note>
          This endpoint only works with schedules that have [email destinations](/share/deliveries/email).
        </Note>
      security:
        - bearerAuth: []
      operationId: removeScheduleRecipients
      parameters:
        - $ref: '#/components/parameters/scheduleId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                emails:
                  type: array
                  items:
                    type: string
                    format: email
                  description: |
                    **At least one email, user ID, or user group ID must be provided.** Array of recipient email addresses to remove from the scheduled task.
                userGroupIds:
                  type: array
                  items:
                    type: string
                  description: |
                    **At least one email, user ID, or user group ID must be provided.** Array of user group IDs to remove as recipients. Use the [List user groups](/api/user-groups/list-user-groups) endpoint to retrieve user group IDs.
                  example: ["X9kQ2mPz"]
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: |
                    **At least one email, user ID, or user group ID must be provided.** Array of recipient membership IDs to remove from the scheduled task. Use the [List users](/api/users#list-users) and [List embed users](/api/users#list-embed-users) endpoints to retrieve user IDs.

                    Each ID removes only the single recipient with that membership ID. This is applicable to embed users: multiple embed users in the same organization can share the same `embed_email`, but removing one by user ID won't remove other users that share the email address. To remove every recipient delivered to a given address, use `emails` instead.
      responses:
        '200':
          description: Recipients removed successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  removedRecipientsCount:
                    type: integer
                    description: The number of recipients removed from the schedule.
                  success:
                    type: boolean
                    description: Indicates whether the operation was successful.
              example:
                removedRecipientsCount: 2
                success: true
        '400':
          description: |
            Bad Request. Possible error messages include:

            - `Bad Request: userIds: Invalid uuid`
            - `Bad Request: userGroupIds: Invalid user group ID` - returned for an unknown ID, a full user group UUID, or an embed user group's ID
            - `Bad Request: emails: Invalid email address`
            - `Please provide either valid email addresses, valid user IDs, or both`
            - `At least one recipient must be provided`
            - `{parameter}: Array must contain at least 1 element(s)`
            - `Invalid recipient(s): The following members do not exist or do not have access to this organization: {userId}`
            - `Cannot change recipients on a scheduled task destination of type {destinationType}`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '404':
          description: |
            Not Found. Possible error messages include:

            - `Scheduled task with id {scheduleId} does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/connections/{connectionId}/schedules:
    get:
      tags:
        - Schema refresh schedules
      summary: List schema refresh schedules
      description: |
        Retrieve all schema refresh schedules configured for the specified connection. Each schedule uses a cron expression and timezone to define when the schema should be refreshed.

        <Note>
          This endpoint requires Connection Admin permissions for the connection.
        </Note>
      security:
        - bearerAuth: []
      operationId: listSchemaRefreshSchedules
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the connection
      responses:
        '200':
          description: Successfully retrieved schema refresh schedules
          content:
            application/json:
              schema:
                type: object
                properties:
                  schedules:
                    type: array
                    items:
                      $ref: '#/components/schemas/SchemaRefreshSchedule'
              example:
                schedules:
                  - scheduleId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    connectionId: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
                    schedule: "00 09 * * ? *"
                    timezone: "America/New_York"
                    description: "At 09:00 AM EDT"
                    hardRefresh: false
                    createdAt: "2025-01-15T10:30:00.000Z"
                    updatedAt: "2025-01-15T10:30:00.000Z"
                    disabledAt: null
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Insufficient permissions. **Connection Admin** permissions for the connection are required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
        - Schema refresh schedules
      summary: Create schema refresh schedule
      description: |
        Create a new schema refresh schedule for the specified connection. Multiple schedules can be created for the same connection.

        <Note>
          This endpoint requires Connection Admin permissions for the connection.
        </Note>
      security:
        - bearerAuth: []
      operationId: createSchemaRefreshSchedule
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the connection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - schedule
                - timezone
              properties:
                schedule:
                  type: string
                  description: |
                    A 6-field cron expression in AWS EventBridge format defining when the schema refresh should run, specified as:

                    ```
                    minute hour day-of-month month day-of-week year
                    ```

                    For example:

                    - `00 09 * * ? *` - Every day at 9:00 AM
                    - `00 18 ? * MON-FRI *` - Every weekday at 6:00 PM
                    - `30 08 1 * ? *` - First day of every month at 8:30 AM
                timezone:
                  type: string
                  description: |
                    An IANA timezone identifier for when the schedule should run, such as `America/New_York`, `Europe/London`, `Asia/Tokyo`
                hardRefresh:
                  type: boolean
                  default: false
                  description: Whether the scheduled refresh performs a hard refresh (removes dropped objects) or soft refresh (additive only). Defaults to false (soft refresh).
            example:
              schedule: "00 09 * * ? *"
              timezone: "America/New_York"
              hardRefresh: false
      responses:
        '201':
          description: Schema refresh schedule created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SchemaRefreshSchedule'
              example:
                scheduleId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                connectionId: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
                schedule: "00 09 * * ? *"
                timezone: "America/New_York"
                description: "At 09:00 AM EDT"
                hardRefresh: false
                createdAt: "2025-01-15T10:30:00.000Z"
                updatedAt: "2025-01-15T10:30:00.000Z"
                disabledAt: null
        '400':
          description: |
            Bad Request

            Possible error messages:
            - Invalid cron expression
            - Invalid timezone
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Insufficient permissions. **Connection Admin** permissions for the connection are required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/connections/{connectionId}/schedules/{scheduleId}:
    get:
      tags:
        - Schema refresh schedules
      summary: Get schema refresh schedule
      description: |
        Retrieve the details of a connection's specific schema refresh schedule.

        <Note>
          This endpoint requires Connection Admin permissions for the connection.
        </Note>
      security:
        - bearerAuth: []
      operationId: getSchemaRefreshSchedule
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the connection
        - name: scheduleId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the schema refresh schedule
      responses:
        '200':
          description: Successfully retrieved schema refresh schedule
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SchemaRefreshSchedule'
              example:
                scheduleId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                connectionId: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
                schedule: "00 09 * * ? *"
                timezone: "America/New_York"
                description: "At 09:00 AM EDT"
                hardRefresh: false
                createdAt: "2025-01-15T10:30:00.000Z"
                updatedAt: "2025-01-15T10:30:00.000Z"
                disabledAt: null
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Insufficient permissions. **Connection Admin** permissions for the connection are required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:
            - Schedule not found
            - Schedule belongs to a different connection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - Schema refresh schedules
      summary: Update schema refresh schedule
      description: |
        Update the schedule and/or timezone for an existing schema refresh schedule.

        <Note>
          This endpoint requires Connection Admin permissions for the connection.
        </Note>
      security:
        - bearerAuth: []
      operationId: updateSchemaRefreshSchedule
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the connection
        - name: scheduleId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the schema refresh schedule
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - schedule
                - timezone
              properties:
                schedule:
                  type: string
                  description: |
                    A 6-field cron expression in AWS EventBridge format defining when the schema refresh should run, specified as:

                    ```
                    minute hour day-of-month month day-of-week year
                    ```

                    For example:

                    - `00 09 * * ? *` - Every day at 9:00 AM
                    - `00 18 ? * MON-FRI *` - Every weekday at 6:00 PM
                    - `30 08 1 * ? *` - First day of every month at 8:30 AM
                timezone:
                  type: string
                  description: |
                    An IANA timezone identifier for when the schedule should run.
                hardRefresh:
                  type: boolean
                  default: false
                  description: Whether the scheduled refresh performs a hard refresh (removes dropped objects) or soft refresh (additive only). Defaults to false (soft refresh).
            example:
              schedule: "00 18 ? * * *"
              timezone: "Europe/London"
              hardRefresh: false
      responses:
        '200':
          description: Schema refresh schedule updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SchemaRefreshSchedule'
              example:
                scheduleId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                connectionId: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
                schedule: "00 18 ? * * *"
                timezone: "Europe/London"
                description: "At 06:00 PM GMT"
                hardRefresh: false
                createdAt: "2025-01-15T10:30:00.000Z"
                updatedAt: "2025-01-15T11:00:00.000Z"
                disabledAt: null
        '400':
          description: |
            Bad Request

            Possible error messages:
            - Invalid cron expression
            - Invalid timezone
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Insufficient permissions. **Connection Admin** permissions for the connection are required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:
            - Schedule not found
            - Connection not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Schema refresh schedules
      summary: Delete schema refresh schedule
      description: |
        Delete a schema refresh schedule. This does not affect other schedules for the same connection.

         <Note>
          This endpoint requires Connection Admin permissions for the connection.
        </Note>
      security:
        - bearerAuth: []
      operationId: deleteSchemaRefreshSchedule
      parameters:
        - name: connectionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the connection
        - name: scheduleId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the schema refresh schedule
      responses:
        '200':
          description: Schema refresh schedule deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
              example:
                success: true
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Insufficient permissions. **Connection Admin** permissions for the connection are required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:
            - Schedule not found
            - Connection not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/view/{viewName}:
    delete:
      tags:
        - Models
      summary: Delete view
      description: |
        Delete or ignores a view in a model. Use the `mode` parameter to specify the behavior of this endpoint.
      security:
        - bearerAuth: []
      operationId: deleteView
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the model.
        - name: viewName
          in: path
          required: true
          schema:
            type: string
            pattern: ^\S*$
          description: The name of the view to delete or ignore. Must not contain whitespace. A trailing `.view` or `.query.view` suffix is ignored.
        - name: branchId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Optional branch ID
        - name: mode
          in: query
          required: false
          schema:
            type: string
            enum: [COMBINED, MERGED, EXTENSION]
            default: EXTENSION
          description: |
            Controls how the view is deleted or hidden:

            - `COMBINED` — **Default**. Marks the view as ignored (`ignored: true`) if it exists in the parent model, otherwise hard-deletes
            - `EXTENSION` — Hard-deletes the view from the extension layer
            - `MERGED` — Marks the view as `shallowIgnored: true`, hiding it only from the immediate parent layer
      responses:
        '200':
          description: View deleted or ignored successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Missing or invalid authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Bad Request: Invalid modelId format`
            - `Bad Request: Invalid mode parameter`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model not found`
            - `View not found`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/models/{modelId}/view/{viewName}/field/{fieldName}:
    patch:
      operationId: modelsUpdateField
      summary: Update field metadata
      description: |
        Update a field's metadata on a view. Omitted properties keep their current value.
      tags:
        - Models
      x-mint:
        content: |
          ## Clearing metadata values

          To clear `label`, `description`, `groupLabel`, `format`, `aiContext`, `synonyms`, or `sampleValues`, send `null` or an empty string (`""`).

          `drillFields` works differently: `null` reverts the field to the view's default drill fields, while an empty list (`[]`) or an empty string removes drill entirely.
        
          Timeframe fields (for example, `created_at[date]`) and a composite topic's shared fields can't be cleared this way and will result in an error.
      parameters:
        - $ref: '#/components/parameters/modelId'
        - name: viewName
          in: path
          required: true
          schema:
            type: string
            pattern: ^\S*$
            example: orders
          description: The name of the view that contains the field. Must not contain whitespace. A trailing `.view` or `.query.view` suffix is ignored.
        - name: fieldName
          in: path
          required: true
          schema:
            type: string
            example: total_amount
          description: The name of the field to update.
        - $ref: '#/components/parameters/branchId'
      requestBody:
        content:
          application/json:
            schema:  # ModelsUpdateFieldBody
              type: object
              properties:
                aiContext:
                  type: string
                  description: AI context for the field; null or an empty string clears it
                allValues:
                  anyOf:
                    - type: array
                      items:
                        type: string
                    - type: string
                    - type: 'null'
                  description: |
                    Deprecated: use `sampleValues` instead. An array, or a comma-separated string; null or an empty string clears them.
                binBoundaries:
                  anyOf:
                    - type: array
                      items:
                        type: number
                    - type: string
                  description: Bin boundaries for binned fields; an array of numbers, or a comma-separated string
                binLabels:
                  anyOf:
                    - type: array
                      items:
                        type: string
                    - type: string
                  description: Labels for bins; an array, or a JSON-encoded array
                description:
                  type: string
                  description: Field description. Pass as `null` or an empty string to clear.
                drillFields:
                  anyOf:
                    - type: array
                      items:
                        type: string
                    - type: string
                    - type: 'null'
                  description: |
                    Drill-down fields as an array or a comma-separated string.

                    Pass an empty list or string to remove drilling, and `null` to revert to the view's default.
                elseRawValue:
                  anyOf:
                    - type: boolean
                    - type: string
                  description: |
                    For grouped fields, values that don't match a group keep their original value instead of `elseValue`.
                elseValue:
                  type: string
                  description: Else value for grouped fields
                filters:
                  anyOf:
                    - type: object
                      additionalProperties:
                        type: object
                        properties:
                          type:
                            type: string
                            enum:
                              - string
                              - number
                              - date
                              - boolean
                              - 'null'
                              - composite
                              - query
                              - user_attribute
                        required:
                          - type
                        additionalProperties: {}
                    - type: string
                  description: Filters for the field, keyed by filter ID.
                format:
                  type: string
                  description: Field format. Pass as `null` or an empty string to clear.
                groupFilters:
                  anyOf:
                    - anyOf:
                        - type: object
                          properties:
                            type:
                              type: string
                              enum:
                                - string
                                - number
                                - date
                                - boolean
                                - 'null'
                                - composite
                                - query
                                - user_attribute
                          required:
                            - type
                          additionalProperties: {}
                        - type: string
                    - type: array
                      items:
                        anyOf:
                          - type: object
                            properties:
                              type:
                                type: string
                                enum:
                                  - string
                                  - number
                                  - date
                                  - boolean
                                  - 'null'
                                  - composite
                                  - query
                                  - user_attribute
                            required:
                              - type
                            additionalProperties: {}
                          - type: string
                  description: |
                    Group filters, one filter per entry in `groupNames`.
                groupLabel:
                  type: string
                  description: |
                    Group label. Pass as `null` or an empty string to clear.
                groupNames:
                  anyOf:
                    - type: string
                    - type: array
                      items:
                        type: string
                  description: Group names
                hidden:
                  anyOf:
                    - type: string
                    - type: boolean
                  description: Whether the field is hidden
                ignored:
                  anyOf:
                    - type: string
                    - type: boolean
                  description: Whether the field is ignored
                isCalc:
                  anyOf:
                    - type: string
                    - type: boolean
                  description: Whether this is a calculation field
                label:
                  type: string
                  description: Field label. Pass as `null` or an empty string to clear.
                newFieldName:
                  type: string
                  description: New field name (for rename)
                newViewName:
                  type: string
                  description: New view name (for move)
                  example: orders
                sampleValues:
                  anyOf:
                    - type: array
                      items:
                        type: string
                    - type: string
                    - type: 'null'
                  description: |
                    Sample values for the field as an array or a comma-separated string. Pass as `null` or an empty string to clear.
                sql:
                  type: string
                  description: SQL expression for the field
                synonyms:
                  anyOf:
                    - type: array
                      items:
                        type: string
                    - type: string
                    - type: 'null'
                  description: Synonyms for the field as an array or a comma-separated string. Pass as `null` or an empty string to clear.
                tags:
                  anyOf:
                    - type: array
                      items:
                        type: string
                    - type: string
                  description: Tags for the field as an array or a comma-separated string.
                topicContext:
                  type: string
                  description: Topic context for the field
            examples:
              partialUpdate:
                summary: Update one property
                value:
                  format: percent_1
              clearProperties:
                summary: Clear properties with null
                value:
                  description: null
                  groupLabel: null
                  format: percent_1
      responses:
        '200':
          description: Field updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: |
            Invalid request body. Common causes:

            - A malformed value for a property, such as `binLabels` or `binBoundaries`
            - `null` sent for a timeframe field (for example, `created_at[date]`) or a composite topic's shared field, which can't be cleared this way
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Permission denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Model, view, or field not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'

  /v1/models/{modelId}/suggestions/{suggestionId}/ignore:
    post:
      tags:
        - AI Model Suggestions
      summary: Dismiss a suggestion
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Dismiss a suggestion, removing it from the active list. Dismissed suggestions can be restored using the [restore endpoint](/api/ai-model-suggestions/restore-a-dismissed-suggestion).
      security:
        - bearerAuth: []
      operationId: modelSuggestionsIgnore
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
          description: The unique identifier of the model.
        - name: suggestionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: b2c3d4e5-f6a7-8901-bcde-f12345678901
          description: The unique identifier of the suggestion to dismiss.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  maxLength: 4000
                  description: Optional free-text reason for dismissing the suggestion.
                  example: Already covered by an existing field description.
              additionalProperties: false
      responses:
        '200':
          description: Suggestion dismissed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Indicates the request was successful
                    example: true
        '400':
          description: Invalid body or malformed ID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            AI disabled, model is not a shared model, or caller lacks Organization Admin permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model not found`
            - `Suggestion not found`
            - Cross-organization suggestion access denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/models/{modelId}/suggestions/{suggestionId}/restore:
    post:
      tags:
        - AI Model Suggestions
      summary: Restore a dismissed suggestion
      description: |
        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>

        Restore a previously dismissed suggestion back to the active list.
      security:
        - bearerAuth: []
      operationId: modelSuggestionsRestore
      parameters:
        - name: modelId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
          description: UUID of the shared model the suggestion belongs to.
        - name: suggestionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: b2c3d4e5-f6a7-8901-bcde-f12345678901
          description: The unique identifier of the suggestion to restore.
      responses:
        '200':
          description: Suggestion restored successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Indicates the request was successful
                    example: true
        '400':
          description: Malformed ID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            AI disabled, model is not a shared model, or caller lacks organization admin permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found

            Possible error messages:

            - `Model not found`
            - `Suggestion not found`
            - Cross-organization suggestion access denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/models/{modelId}/topic:
    get:
      operationId: modelsListTopics
      summary: List topics
      description: |
        Retrieve a list of topics in the model, including composite topics.
      tags:
        - Topics
      parameters:
        - $ref: '#/components/parameters/modelId'
        - $ref: '#/components/parameters/branchId'
      responses:
        '200':
          description: List of topics in the model
          content:
            application/json:    # ModelsListTopicsResponse
              schema:
                required:
                  - success
                  - topics
                type: object
                properties:
                  success:
                    type: boolean
                    description: Whether the operation succeeded
                  topics:
                    type: array
                    description: List of topic objects
                    items:
                      anyOf:
                        - title: "Regular topic"
                          type: object
                          properties:
                            base_view_name:
                              $ref: '#/components/schemas/TopicBaseViewName'
                            description:
                              $ref: '#/components/schemas/TopicDescription'
                            group_label: 
                              type: string
                              description: Group label
                            hidden:
                              $ref: '#/components/schemas/TopicHidden'
                            is_composite:
                              $ref: '#/components/schemas/TopicIsComposite'
                            label:
                              $ref: '#/components/schemas/TopicLabel'
                            name:
                              $ref: '#/components/schemas/TopicName'
                        - title: "Composite topic"
                          type: object
                          properties:
                            description:
                              $ref: '#/components/schemas/TopicDescription'
                            hidden:
                              $ref: '#/components/schemas/TopicHidden'
                            is_composite:
                              $ref: '#/components/schemas/TopicIsComposite'
                            label:
                              $ref: '#/components/schemas/TopicLabel'
                            name:
                              $ref: '#/components/schemas/TopicName'
                            import_models:
                              $ref: '#/components/schemas/CompositeTopicImportModels'
                            topics:
                              $ref: '#/components/schemas/CompositeTopicTopicList'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Permission denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Model not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'

  /v1/models/{modelId}/topic/{topicName}:
    get:
      tags:
        - Topics
      summary: Retrieve a topic
      description: Retrieve a topic in a model by name.
      security:
        - bearerAuth: []
      operationId: getTopic
      parameters:
        - $ref: '#/components/parameters/modelId'
        - $ref: '#/components/parameters/topicName'
      responses:
        '200':
          description: ""
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    description: Indicates the request was successful
                  topic:
                    description: Details about the topic. Regular topics mirror the [IDE parameters](/modeling/topics/parameters) for topic files. When `topicName` resolves to a composite topic, the response mirrors the [composite topic IDE parameters](/modeling/composite-topics/parameters) instead.
                    anyOf:
                      - title: "Regular topic"
                        $ref: '#/components/schemas/TopicObject'
                      - title: "Composite topic"
                        $ref: '#/components/schemas/CompositeTopicObject'
              examples:
                regularTopic:
                  summary: Regular topic
                  value:
                    success: true
                    topic:
                      name: "Customers"
                      base_view_name: "main__customers"
                      label: "Customers"
                      default_filters: {}
                      join_via_map:
                        main__orders: []
                        main__order_items:
                          - "main__orders"
                      join_via_map_key_order:
                        - "main__orders"
                        - "main__order_items"
                      ignored_props: []
                      always_where_filters: {}
                      extension_model_id: "5da4e30e-45e2-4693-b87d-51c39d69963f"
                      has_frozen_join_via_map: true
                      relationships:
                        - left_view_name: "main__orders"
                          right_view_name: "main__customers"
                          join_type: "ALWAYS_LEFT"
                          sql: "${main__orders.customer_id} = ${main__customers.id}"
                          id: "main__orders_main__customers"
                          type: "ASSUMED_MANY_TO_ONE"
                          ignored: false
                          bidirectional: false
                      ide_file_name: "Customers.topic"
                      views:
                        - name: "main__customers"
                          label: "Customers"
                          table_name: "customers"
                          schema: "main"
                          schema_label: "Main"
                          extension_model_id: "5da4e30e-45e2-4693-b87d-51c39d69963f"
                          ide_file_name: "main/customers.view"
                          yaml_path: "main__customers.view"
                          filter_only_fields: []
                          is_pseudo_display_view: false
                          primary_key:
                            - type: "field"
                              field_name: "main__customers.id"
                          dimensions:
                            - field_name: "country"
                              view_name: "main__customers"
                              data_type: "STRING"
                              description: "User's country"
                              view_label: "Customers"
                              is_dimension: true
                              fully_qualified_name: "main__customers.country"
                            - field_name: "created_date"
                              view_name: "main__customers"
                              data_type: "TIMESTAMP"
                              time_frames:
                                - null
                                - "DATE"
                                - "WEEK"
                                - "MONTH"
                                - "QUARTER"
                                - "YEAR"
                              date_type: "DATE"
                              description: "Date of user creation"
                              parent_field: "created_date"
                              parent_label: "Created Date"
                              view_label: "Customers"
                              group_label: "Created Date"
                              is_dimension: true
                              is_group_parent_field: true
                              fully_qualified_name: "main__customers.created_date"
                          measures:
                            - type: "aggregation"
                              field_name: "count"
                              view_name: "main__customers"
                              aggregate_type: "COUNT"
                              data_type: "NUMBER"
                              ignored: false
                              label: "Customers Count"
                              format: "NUMBER_0"
                              view_label: "Customers"
                              display_sql: "COUNT(*)"
                              dialect_sql: "COUNT(*)"
                              fully_qualified_name: "main__customers.count"
                compositeTopic:
                  summary: Composite topic
                  value:
                    success: true
                    topic:
                      is_composite: true
                      name: "orders_marketing_composite"
                      label: "Orders & Marketing"
                      description: "Revenue and marketing performance, aligned by date"
                      topics:
                        - "orders_topic"
                        - "marketing_topic"
                      shared_dimensions:
                        activity_date:
                          label: "Activity date"
                          mappings:
                            orders_topic:
                              field: "orders.created_at"
                            marketing_topic:
                              field: "campaigns.start_date"
                      shared_views:
                        - "date_spine"
                      default_filters:
                        activity_date:
                          time_for_duration: "30 days ago for 30 days"
                      sample_queries:
                        Cross-Topic Summary:
                          query:
                            fields:
                              - "shared_date.month"
                              - "order_items.sale_price_sum"
                              - "marketing.spend_sum"
                            base_view: "orders_marketing_composite"
                          description: "Monthly revenue and marketing spend"
                      import_models:
                        - model_ref: "5da4e30e-45e2-4693-b87d-51c39d69963f"
                          topics:
                            - topic: "regional_sales"
                              alias: "regional_sales_topic"
                              ignored: false
        '400':
          description: |
            Bad Request. Possible causes:

            - Invalid model UUID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Model with id <modelId> does not exist"
                status: 400
        '404':
          description: |
            Not Found. Possible causes:

            - Model not found
            - Topic not found or access not permitted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                modelNotFound:
                  summary: Model not found
                  value:
                    detail: "Model with id <modelId> does not exist"
                    status: 404
                topicNotFound:
                  summary: Topic not found (name doesn't match a regular or composite topic)
                  value:
                    detail: "No such topic \"<topicName>\" or access not permitted"
                    status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
    patch:
      operationId: modelsUpdateTopic
      summary: Update topic
      description: |
        Update an existing topic.
      tags:
        - Topics
      parameters:
        - $ref: '#/components/parameters/modelId'
        - $ref: '#/components/parameters/topicName'
        - $ref: '#/components/parameters/branchId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                description:
                  $ref: '#/components/schemas/TopicDescription'
                groupLabel:
                  $ref: '#/components/schemas/TopicGroupLabel'
                hidden:
                  $ref: '#/components/schemas/TopicHidden'
                label:
                  $ref: '#/components/schemas/TopicLabel'
                newTopicName:
                  type: string
                  description: The new name of the topic, if performing a rename.
                  example: "2026 Blob Roadmap"
      responses:
        '200':
          description: Topic updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Permission denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Model or topic not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
    delete:
      operationId: modelsDeleteTopic
      summary: Delete topic
      description: |
        Delete a topic from a model.
      tags:
        - Topics
      parameters:
        - $ref: '#/components/parameters/modelId'
        - $ref: '#/components/parameters/topicName'
        - $ref: '#/components/parameters/branchId'
        - name: mode
          schema:
            type: string
            enum:
              - COMBINED
              - MERGED
              - EXTENSION
            default: COMBINED
            example: COMBINED
          required: false
          description: |
            Controls delete behavior to match IDE editing modes:
            
            - `COMBINED` - **Default.** Adds the topic to `deletedTopics` if it exists in the parent model
            - `MERGED` behaves the same as `COMBINED`
            - `EXTENSION` hard-deletes the topic from the extension layer
          in: query
      responses:
        '200':
          description: Topic deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Permission denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Model or topic not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'

  /v1/skills:
    get:
      summary: List skills
      operationId: skillsList
      description: |
        <Note>
          This endpoint is currently in development and may change.
        </Note>

        List skills accessible to the calling user. If the caller is an Organization Admin, the response will include all skills in the organization; otherwise, only skills the caller created are returned.
      security:
        - bearerAuth: []
      tags:
        - Skills
      x-experimental: true
      parameters:
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/pageSize'
        - $ref: '#/components/parameters/sortDirection'
        - name: sortField
          schema:
            type: string
            enum:
              - createdAt
              - name
              - updatedAt
            default: updatedAt
          required: false
          description: Field to sort results by
          in: query
        - name: userId
          schema:
            type: string
            format: uuid
          required: false
          description: |
            **Organization API key required.** Target user membership ID.
          in: query
        - name: creatorId
          schema:
            type: string
            format: uuid
          required: false
          description: |
            Filter by creator membership ID — `creator.id` from a listed skill, or your own `membershipId` from the [Who am I? endpoint](/api/who-am-i/get-current-identity-and-permissions). Applies within what the caller can already see.
          in: query
        - $ref: '#/components/parameters/skillId'
        - name: q
          schema:
            type: string
            maxLength: 200
            example: quarterly
          required: false
          description: |
            Case-insensitive, free-text search over `name`, `handle`, and `description`. Can be used with `identifier` and `creatorId` to further refine results.
          in: query
      responses:
        '200':
          description: Paginated list of skills
          content:
            application/json:
              schema:
                type: object
                required:
                  - pageInfo
                  - records
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    items:   # SkillSummary
                      type: object
                      required:
                        - createdAt
                        - creator
                        - description
                        - id
                        - identifier
                        - name
                        - updatedAt
                      properties:
                        createdAt:
                          $ref: '#/components/schemas/SkillCreatedAt'
                        creator:
                          $ref: '#/components/schemas/SkillCreator'
                        description:
                          $ref: '#/components/schemas/SkillDescription'
                        id:
                          $ref: '#/components/schemas/SkillId'
                        identifier:
                          $ref: '#/components/schemas/SkillIdentifier'
                        name:
                          $ref: '#/components/schemas/SkillName'
                        updatedAt:
                          $ref: '#/components/schemas/SkillUpdatedAt'
              example:
                pageInfo:
                  hasNextPage: false
                  nextCursor: null
                  pageSize: 20
                  totalRecords: 1
                records:
                  - id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                    identifier: quarterly-report
                    name: Quarterly report
                    description: Summarizes the quarter and flags anything off trend.
                    creator:
                      id: b2c3d4e5-f6a7-8901-bcde-f12345678901
                      name: Blob Ross
                    createdAt: "2026-01-15T10:00:00.000Z"
                    updatedAt: "2026-02-01T09:30:00.000Z"
        '400':
          description: Invalid query parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: Invalid bearer token, or skills are not enabled for the caller
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: |
            `creatorId` names no member of this organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
    post:
      description: |
        <Note>
          This endpoint is currently in development and may change.
        </Note>

        Creates a skill owned by the calling user.
      security:
        - bearerAuth: []
      operationId: skillsCreate
      summary: Create a skill
      tags:
        - Skills
      x-experimental: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SkillCreateBody'
            example:
              identifier: quarterly-report
              name: Quarterly report
              description: Summarizes the quarter and flags anything off trend.
              body: "Pull revenue by month for the last four quarters, then..."
      responses:
        '201':
          description: The created skill
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Skill'
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                identifier: quarterly-report
                name: Quarterly report
                description: Summarizes the quarter and flags anything off trend.
                body: "Pull revenue by month for the last four quarters, then..."
                creator:
                  id: b2c3d4e5-f6a7-8901-bcde-f12345678901
                  name: Blob Ross
                createdAt: "2026-01-15T10:00:00.000Z"
                updatedAt: "2026-01-15T10:00:00.000Z"
        '400':
          description: Invalid body, a blank field, or a value over its length limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: Invalid bearer token, or skills are not enabled for the caller
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '405':
          description: Method not allowed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The identifier is already taken in this organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/skills/{skillId}:
    get:
      description: |
        <Note>
          This endpoint is currently in development and may change.
        </Note>

        Retrieve a skill by its UUID.
      security:
        - bearerAuth: []
      operationId: skillsGet
      summary: Get a skill
      tags:
        - Skills
      x-experimental: true
      parameters:
        - name: skillId
          schema:
            type: string
            format: uuid
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
          required: true
          description: Skill UUID
          in: path
        - name: userId
          schema:
            type: string
            format: uuid
          required: false
          description: |
            **Requires an Organization API key.** The membership ID of the user to attribute the action to.
          in: query
      responses:
        '200':
          description: The requested skill
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Skill'
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                identifier: quarterly-report
                name: Quarterly report
                description: Summarizes the quarter and flags anything off trend.
                body: "Pull revenue by month for the last four quarters, then..."
                creator:
                  id: b2c3d4e5-f6a7-8901-bcde-f12345678901
                  name: Blob Ross
                createdAt: "2026-01-15T10:00:00.000Z"
                updatedAt: "2026-02-01T09:30:00.000Z"
        '400':
          description: Malformed `skillId` (must be a UUID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Skills are not enabled for the caller, or the skill belongs to someone else and the caller is not an Organization Admin.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: No skill with this ID in the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'

    patch:
      description: |
        <Note>
          This endpoint is currently in development and may change.
        </Note>

        Updates an existing skill. Only included fields are updated.
      security:
        - bearerAuth: []
      operationId: skillsUpdate
      summary: Edit a skill
      tags:
        - Skills
      x-experimental: true
      parameters:
        - name: skillId
          schema:
            type: string
            format: uuid
            description: Skill UUID
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
          required: true
          description: Skill UUID
          in: path
        - name: userId
          schema:
            type: string
            format: uuid
          required: false
          description: |
            **Requires an Organization API key.** The membership ID of the user to attribute the action to.
          in: query
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SkillUpdateBody'
            example:
              name: Quarterly report (v2)
      responses:
        '200':
          description: The updated skill
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Skill'
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                identifier: quarterly-report
                name: Quarterly report (v2)
                description: Summarizes the quarter and flags anything off trend.
                body: "Pull revenue by month for the last four quarters, then..."
                creator:
                  id: b2c3d4e5-f6a7-8901-bcde-f12345678901
                  name: Blob Ross
                createdAt: "2026-01-15T10:00:00.000Z"
                updatedAt: "2026-02-01T09:30:00.000Z"
        '400':
          description: Invalid body, malformed `skillId`, an empty update, a blank field, or a value over its length limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Skills are not enabled for the caller, or the skill belongs to someone else and the caller is not an Organization Admin.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: No skill with this ID in the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The identifier is already taken in this organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'

    delete:
      description: |
        <Note>
          This endpoint is currently in development and may change.
        </Note>

        Soft-deletes a skill and makes its handle (`/quarterly-report`) available for reuse.
      security:
        - bearerAuth: []
      operationId: skillsDelete
      summary: Delete a skill
      tags:
        - Skills
      x-experimental: true
      parameters:
        - name: skillId
          schema:
            type: string
            format: uuid
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
          required: true
          description: Skill UUID
          in: path
        - name: userId
          schema:
            type: string
            format: uuid
          required: false
          description: |
            **Requires an Organization API key.** The membership ID of the user to attribute the action to.
          in: query
      responses:
        '200':
          description: The skill was deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SkillDeleteResponse'
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                deletedAt: "2026-02-01T09:30:00.000Z"
        '400':
          description: Malformed `skillId` (must be a UUID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '403':
          description: |
            Skills are not enabled for the caller, or the skill belongs to someone else and the caller is not an Organization Admin.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: No skill with this ID in the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/uploads:
    get:
      tags:
        - Uploads
      summary: List uploads
      description: |
        List all uploads ([CSV files and spreadsheets](/analyze-explore/data-input-csvs)) in the organization with metadata and optional filtering.

        This endpoint requires **Organization Admin** permissions.
      security:
        - bearerAuth: []
      operationId: listUploads
      parameters:
        - name: type
          in: query
          schema:
            type: string
            enum:
              - csv
              - spreadsheet
            default: csv
          description: Filter by upload type.
        - name: connectionId
          in: query
          schema:
            type: string
            format: uuid
          description: Filter by connection ID.
        - name: modelId
          in: query
          schema:
            type: string
            format: uuid
          description: Filter by model ID. Shared models return non-private connection uploads; workbook models return their own uploads.
        - name: searchTerm
          in: query
          schema:
            type: string
          description: Search term to filter by file name (case-insensitive).
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Number of items to return.
        - name: cursor
          in: query
          schema:
            type: string
          description: Cursor for pagination (from previous response).
        - name: sortField
          in: query
          schema:
            type: string
            enum:
              - createdAt
              - fileName
              - updatedAt
            default: updatedAt
          description: Field to sort by.
        - name: sortDirection
          in: query
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
          description: Sort direction.
      responses:
        '200':
          description: Uploads retrieved successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          description: Unique ID for the upload.
                        file_name:
                          type: string
                          description: Original file name.
                        view_name:
                          type: string
                          description: View name in the model.
                        connection_id:
                          type: string
                          format: uuid
                          description: ID of the connection associated with the upload.
                        in_db_as_table_name:
                          type: string
                          nullable: true
                          description: |
                            **Requires that the connection have a defined table upload schema.** The name of the database table associated with the upload.
                        model_id:
                          type: string
                          format: uuid
                          nullable: true
                          description: ID of the model the upload is associated with. Inferred from connection's shared model if not explicitly set via query parameter.
                        size_bytes:
                          type: integer
                          nullable: true
                          description: File size in bytes.
                        created_at:
                          type: string
                          format: date-time
                          description: ISO 8601 timestamp of when the upload was created.
                        updated_at:
                          type: string
                          format: date-time
                          description: ISO 8601 timestamp of when the upload was last updated.
                        uploaded_by_user:
                          type: object
                          description: User who uploaded the file.
                          properties:
                            id:
                              type: string
                              format: uuid
                              nullable: true
                              description: Membership ID. `null` when the uploader no longer has an active membership in the organization.
                            name:
                              type: string
                              description: User display name.
              example:
                pageInfo:
                  hasNextPage: false
                  nextCursor: null
                  pageSize: 20
                  totalRecords: 2
                records:
                  - id: 550e8400-e29b-41d4-a716-446655440000
                    file_name: users.csv
                    view_name: users
                    connection_id: 660e8400-e29b-41d4-a716-446655440001
                    in_db_as_table_name: omni_upload_t550e8400
                    model_id: 880e8400-e29b-41d4-a716-446655440003
                    size_bytes: 1024
                    created_at: "2025-01-15T10:00:00Z"
                    updated_at: "2025-01-15T10:00:00Z"
                    uploaded_by_user:
                      id: 770e8400-e29b-41d4-a716-446655440002
                      name: John Doe
                  - id: 660e8400-e29b-41d4-a716-446655440001
                    file_name: legacy_orders.csv
                    view_name: legacy_orders
                    connection_id: 660e8400-e29b-41d4-a716-446655440001
                    in_db_as_table_name: omni_upload_t660e8400
                    model_id: 880e8400-e29b-41d4-a716-446655440003
                    size_bytes: 2048
                    created_at: "2024-06-02T09:30:00Z"
                    updated_at: "2024-06-02T09:30:00Z"
                    uploaded_by_user:
                      id: null
                      name: Jane Smith
        '400':
          description: |
            Bad Request. Possible error messages include:

            - `connectionId: Invalid uuid`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Bad Request: connectionId: Invalid uuid"
                status: 400
        '401':
          description: Missing or invalid authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions. Requires `MANAGE_UPLOADS` permission.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: You do not have permission to perform this action
                status: 403
        '404':
          description: Model not found (invalid `modelId`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
        - Uploads
      summary: Upload a CSV file
      description: |
        <Note>
          To use this endpoint:
          
          - The **Upload data** setting in **Settings > Content permissions** must enabled by an **Organization Admin**
          - The authenticating user must have **Restricted Querier** permissions or higher on the model the file will be uploaded to
        </Note>

        Upload a CSV file to create a new [data input table](/analyze-explore/data-input-csvs). The file is parsed, converted to Arrow format, uploaded to storage, written to the connection's table upload (scratch) schema, and a view is created in the specified model.

        Uploaded files:

        - Must be a CSV file with `.csv` extension
        - Can have a maximum of 500,000 rows. Files will be truncated if this limit is exceeded.

        The returned `id` can be passed as `fileUploadId` on a `csv` or `spreadsheet` queryPresentation tab via the [Documents v2 draft-patch endpoints](/api/documents-v2/create-draft-and-patch-document#binding-an-upload-as-a-source-tab) to bind the upload as a document Source tab.
      security:
        - bearerAuth: []
      operationId: uploadCsvFile
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
                - modelId
              properties:
                file:
                  type: string
                  format: binary
                  description: The CSV file to upload, which must have a `.csv` extension
                modelId:
                  type: string
                  format: uuid
                  description: UUID of the model to create the view in
                  example: 880e8400-e29b-41d4-a716-446655440003
                branchId:
                  type: string
                  format: uuid
                  description: UUID of the branch to create the view in. Mutually exclusive with `branchName`.
                  example: 990e8400-e29b-41d4-a716-446655440004
                branchName:
                  type: string
                  description: Name of the branch to create the view in. Mutually exclusive with `branchId`.
                  example: my-branch
                viewName:
                  type: string
                  description: Override the view name. Defaults to sanitized file name, suffixed with `_2`, `_3`, and so on if the model already has a view by that name.
                  example: custom_view_name
            encoding:
              file:
                contentType: text/csv
      responses:
        '201':
          description: CSV file uploaded successfully and view created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: Unique identifier for the upload
                  fileName:
                    type: string
                    description: Original file name
                  viewName:
                    type: string
                    description: Name of the view created. May differ from the requested or derived name if that name was already taken.
                  modelId:
                    type: string
                    format: uuid
                    description: ID of the model the view was created in
                  inDbAsTableName:
                    type: string
                    description: Database table name in the scratch schema
                  rowCount:
                    type: integer
                    description: Number of rows in the uploaded file
                  truncated:
                    type: boolean
                    description: Whether the file was truncated due to row limit (500,000 rows)
                  viewCreated:
                    type: boolean
                    description: Whether a view was created in the model
              example:
                id: 550e8400-e29b-41d4-a716-446655440000
                fileName: users.csv
                viewName: users
                modelId: 880e8400-e29b-41d4-a716-446655440003
                inDbAsTableName: omni_upload_t550e8400
                rowCount: 150
                truncated: false
                viewCreated: true
        '400':
          description: |
            Bad Request. Possible error messages include:

            - Missing required fields (`file` or `modelId`)
            - Invalid file type (not a CSV file)
            - CSV parsing failed
            - Invalid UUID format
            - Both `branchId` and `branchName` provided (mutually exclusive)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Bad Request: file must have .csv extension"
                status: 400
        '401':
          description: Missing or invalid authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Unauthorized"
                status: 401
        '403':
          description: Insufficient permissions. Authenticating user must have **Restricted Querier** permissions or higher on the model.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "You do not have permission to perform this action"
                status: 403
        '404':
          description: |
            Not Found. Possible error messages include:

            - Model not found (invalid `modelId`)
            - Branch not found (invalid `branchId` or `branchName`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Model not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/uploads/{uploadId}:
    delete:
      tags:
        - Uploads
      summary: Delete an upload
      description: |
        Delete a CSV upload by its ID. This removes the file from storage and marks the record as deleted.

        <Note>
          This endpoint requires **Organization Admin** permissions.
        </Note>
      security:
        - bearerAuth: []
      operationId: deleteUpload
      parameters:
        - name: uploadId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the upload to delete
      responses:
        '200':
          description: Upload deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
              example:
                success: true
        '400':
          description: Invalid upload ID format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Bad Request: uploadId: Invalid uuid"
                status: 400
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions. Requires **Organization Admin** permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "You do not have permission to perform this action"
                status: 403
        '404':
          description: Upload not found or already deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Upload not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - Uploads
      summary: Replace upload data
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>
        
        Replace the data in an existing CSV upload.

        The view's [`uploaded_table_name`](/modeling/views/parameters/uploaded-table-name) property and references to it (returned as `refreshedModelIds`) will automatically update, but the upload's ID will remain unchanged. This means that any documents, queries, views, etc. using the upload will be re-pointed to the new data. The previous data's artifacts are left untouched.
      x-mint:
        content: |
          <Warning>
            Renaming or removing columns may break content (fields, calculations, filters) referencing the old columns.
          </Warning>

          ## Column data types

          Columns already in the data input table keep their saved type and date format, and new columns are detected from the file. A value that doesn't adhere to its column's saved data type fails the request. To change a column's type, delete the upload and create it again.
      security:
        - bearerAuth: []
      operationId: replaceUploadData
      parameters:
        - name: uploadId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The unique identifier of the upload
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  description: The replacement CSV file (must have `.csv` extension). Fully replaces the existing data; existing columns keep their saved type and date format. Column renames/removals may break content referencing the old columns.
                  format: binary
              required:
                - file
              additionalProperties: false
            encoding:
              file:
                contentType: text/csv
      responses:
        '200':
          description: Upload data replaced successfully
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                  - fileName
                  - viewName
                  - inDbAsTableName
                  - refreshedModelIds
                  - rowCount
                  - truncated
                properties:
                  id:
                    type: string
                    format: uuid
                    description: The upload ID. This will not change after uploading new data.
                    example: "550e8400-e29b-41d4-a716-446655440000"
                  fileName:
                    type: string
                    description: File name of the replacement upload
                    example: users_updated.csv
                  viewName:
                    type: string
                    description: View name. This will not change after uploading new data.
                    example: users
                  inDbAsTableName:
                    type: string
                    description: Database table name in the table upload (scratch) schema. This will be empty if the underlying connection doesn't have a table upload schema configured.
                    example: omni_upload_t7c1e22ab
                  refreshedModelIds:
                    type: array
                    items:
                      type: string
                      format: uuid
                    description: Models whose view of this upload was re-pointed at the new data, including the upload's workbook model, or the connection's shared models holding a view backed by this upload.
                    example: ["880e8400-e29b-41d4-a716-446655440003"]
                  rowCount:
                    type: integer
                    description: Number of rows in the replacement file
                    example: 152
                  truncated:
                    type: boolean
                    description: Whether the file was truncated due to row limit
                    example: false
              example:
                id: "550e8400-e29b-41d4-a716-446655440000"
                fileName: "users_updated.csv"
                viewName: "users"
                inDbAsTableName: "omni_upload_t7c1e22ab"
                refreshedModelIds: ["880e8400-e29b-41d4-a716-446655440003"]
                rowCount: 152
                truncated: false
        '400':
          description: |
            Invalid request. Possible causes:
            
            - Missing file
            - Non-CSV file
            - Parse failure
            - A value that doesn't adhere to its column's saved type
            - Attempting to replace a non-CSV upload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              examples:
                invalidId:
                  summary: Invalid upload ID
                  value:
                    detail: "Bad Request: uploadId: Invalid UUID"
                    status: 400
                nonCsv:
                  summary: Non-CSV file
                  value:
                    detail: "File must be a CSV file"
                    status: 400
                nonCsvUpload:
                  summary: Spreadsheet-backed upload
                  value:
                    detail: "Only CSV uploads can be replaced through this endpoint"
                    status: 400
                typeMismatch:
                  summary: Value doesn't fit the column's saved type
                  value:
                    detail: "CSV parsing failed: Row 3: \"abc\" in id isn't a whole number"
                    status: 400
        '401':
          description: Missing or invalid authentication
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions. Requires permissions to upload data.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "You do not have permission to perform this action"
                status: 403
        '404':
          description: Upload not found or already deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "Upload not found"
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/users/email-only:
    get:
      tags:
        - Users
      summary: List email-only users
      description: List email-only users and their user attributes. Supports pagination and filtering.
      security:
        - bearerAuth: []
      operationId: listEmailOnlyUsers
      parameters:
        - name: email
          in: query
          schema:
            type: string
          description: Filter users by email (partial match, case-insensitive)
        - name: cursor
          in: query
          schema:
            type: string
          description: Pagination cursor from previous response
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 20
            maximum: 20
          description: Results per page
        - name: sortDirection
          in: query
          schema:
            type: string
            enum: [asc, desc]
            default: desc
          description: |
            Sort order:
            - `asc` - Ascending order (A-Z)
            - `desc` - Descending order (Z-A)
      responses:
        '200':
          description: Paginated list of email-only users
          content:
            application/json:
              schema:
                type: object
                properties:
                  pageInfo:
                    $ref: '#/components/schemas/PageInfo'
                  records:
                    type: array
                    items:
                      type: object
                      properties:
                        email:
                          type: string
                          format: email
                          description: User's email address
                        user_id:
                          type: string
                          format: uuid
                          description: User's membership ID
                        user_attributes:
                          type: object
                          description: Key-value map of user attributes, including system attributes like `omni_user_email`, `omni_user_name`, `omni_user_id`, `omni_is_org_admin`, plus any custom attributes.
                          additionalProperties:
                            type: string
              example:
                pageInfo:
                  hasNextPage: true
                  nextCursor: "bob@example.com"
                  pageSize: 20
                  totalRecords: 50
                records:
                  - email: "alice@example.com"
                    user_id: "550e8400-e29b-41d4-a716-446655440000"
                    user_attributes:
                      omni_user_email: "alice@example.com"
                      omni_user_name: "alice@example.com"
                      omni_user_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                      omni_is_org_admin: "false"
                      omni_user_timezone: "America/New_York"
                      department: "Engineering"
                  - email: "bob@example.com"
                    user_id: "550e8400-e29b-41d4-a716-446655440001"
                    user_attributes:
                      omni_user_email: "bob@example.com"
                      omni_user_name: "bob@example.com"
                      omni_user_id: "b2c3d4e5-f6a7-8901-bcde-f23456789012"
                      omni_is_org_admin: "false"
                      omni_user_timezone: "Europe/London"
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      tags:
        - Schedule recipients
      summary: Manage email-only user
      description: |
        Create or update an [email-only user](/share/deliveries/email). An email-only user is a recipient of a delivery that isn't associated with an Omni account.

        If Omni matches the provided email to an existing email-only user, the user will be updated according to the `userAttributes` in the request body.

        <Tip>
          Need to include multiple email-only users? Use the [Create or update multiple email-only users](/api/schedule-recipients/bulk-manage-email-only-users) endpoint.
        </Tip>
      security:
        - orgApiKey: []
      operationId: manageEmailOnlyUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                  description: The user's email address.
                userAttributes:
                  type: object
                  description: |
                    An object defining the user's [user attributes](/administration/users/attributes). Attributes are represented as key/value pairs, where the keys map to the IDs of user attributes (the **Reference** column in the **User attributes** page) defined in Omni.

                    When providing user attributes, note that:

                    - `omni_user_timezone` is the only supported system attribute
                    - **Values must match the user attribute's specified type**. For example, `number` attribute values must be numbers such as `1`, `10`, etc.
                    - **Values can also be booleans**. A boolean is stored as `"true"` or `"false"`, so only a `string` attribute accepts it; a `number` attribute rejects it.
                    - Numbers are converted to strings on receipt, so send anything beyond the JSON safe integer range (`2^53 - 1`) as a string or it loses precision before Omni sees it.
                    - **Multi-value attributes should be provided using arrays**. For example: `["US","EU"]` or `[1, 10]`

                    To unset attributes:

                    - `null` values
                    - `""` - Empty strings for string attributes
                    - `[]` - Empty arrays for multi-value attributes
            examples:
              basic:
                summary: Basic request
                value:
                  email: "iamagoodblob@blobsrus.co"
              withAttributes:
                summary: Set user attributes
                value:
                  email: "iamagoodblob@blobsrus.co"
                  userAttributes:
                    region: ["US", "EU"]
                    omni_user_timezone: "America/New_York"
                    is_admin: 0
                    is_sales_team: 1
              unsetAttributes:
                summary: Unset user attributes
                value:
                  email: "iamagoodblob@blobsrus.co"
                  userAttributes:
                    is_admin: null
                    is_sales_team: ""
                    region: []
      responses:
        '200':
          description: Email-only user created or updated successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  email:
                    type: string
                    format: email
                    description: The email address of the created or updated user.
                  userId:
                    type: string
                    format: uuid
                    description: The unique identifier of the email-only user.
              example:
                email: "iamagoodblob@blobsrus.co"
                userId: "9e8719d9-276a-4964-9395-a493189a247c"
        '400':
          description: |
            Bad Request. Possible error messages include:

            - `Invalid JSON`
            - `email: email is required`
            - `email: Invalid email address`
            - `The provided user attributes: "<attribute>" do not match the names of existing user attributes.`
            - `User Attribute <attribute> is type number, but passed-in value <value> is not a number.`
            - `The timezone <invalidTimezone> is not supported. Please see the connection page or [user profile page](/administration/users/your-account) for the list of valid timezones. Use e.g. "America/New_York" instead of "EST"`
            - `User Attribute <attribute> has multiple values enabled, but the passed-in value is not an array. If passing a single value for a user attribute with multiple values enabled, please wrap the value in an array [x].`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/users/email-only/bulk:
    post:
      tags:
        - Schedule recipients
      summary: Bulk manage email-only users
      description: |
        Create or update up to 20 [email-only users](/share/deliveries/email). An email-only user is a recipient of a delivery that isn't associated with an Omni account.

        If Omni matches a provided email to an existing email-only user, the user will be updated according to the `userAttributes` in the request body.
      security:
        - orgApiKey: []
      operationId: bulkManageEmailOnlyUsers
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - users
              properties:
                users:
                  type: array
                  maxItems: 20
                  description: |
                    An array of user objects, which includes `email` and `userAttributes` properties. Up to 20 users can be included in each request.
                  items:
                    type: object
                    required:
                      - email
                    properties:
                      email:
                        type: string
                        format: email
                        description: The user's email address.
                      userAttributes:
                        type: object
                        description: |
                          An object defining the user's [user attributes](/administration/users/attributes). Attributes are represented as key/value pairs, where the keys map to the IDs of user attributes (the **Reference** column in the **User attributes** page) defined in Omni.

                          When providing user attributes, note that:

                          - `omni_user_timezone` is the only supported system attribute
                          - **Values must match the user attribute's specified type**. For example, `number` attribute values must be numbers such as `1`, `10`, etc.
                          - **Values can also be booleans**. A boolean is stored as `"true"` or `"false"`, so only a `string` attribute accepts it; a `number` attribute rejects it.
                          - Numbers are converted to strings on receipt, so send anything beyond the JSON safe integer range (`2^53 - 1`) as a string or it loses precision before Omni sees it.
                          - **Multi-value attributes should be provided using arrays**. For example: `["US","EU"]` or `[1, 10]`

                          To unset attributes:

                          - `null` values
                          - `""` - Empty strings for string attributes
                          - `[]` - Empty arrays for multi-value attributes
            examples:
              basic:
                summary: Basic request
                value:
                  users:
                    - email: "iamagoodblob@blobsrus.co"
                    - email: "blobmanager@blobsrus.co"
              withAttributes:
                summary: Set user attributes
                value:
                  users:
                    - email: "iamagoodblob@blobsrus.co"
                      userAttributes:
                        region: ["US", "EU"]
                        omni_user_timezone: "America/New_York"
                        is_admin: 0
                        is_sales_team: 1
                    - email: "blobmanager@blobsrus.co"
                      userAttributes:
                        region: ["US"]
                        omni_user_timezone: "America/New_York"
                        is_admin: 1
                        is_sales_team: 0
              unsetAttributes:
                summary: Unset user attributes
                value:
                  users:
                    - email: "iamagoodblob@blobsrus.co"
                      userAttributes:
                        region: []
                        omni_user_timezone: ""
                        is_admin: null
      responses:
        '200':
          description: Email-only users created or updated successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    description: An array of created or updated email-only users.
                    items:
                      type: object
                      properties:
                        email:
                          type: string
                          format: email
                          description: The email address of the created or updated user.
                        userId:
                          type: string
                          format: uuid
                          description: The unique identifier of the email-only user.
              example:
                results:
                  - email: "iamagoodblob@blobsrus.co"
                    userId: "1e23dadc-961b-4a21-b36b-17168130fc3f"
                  - email: "blobmanager@blobsrus.co"
                    userId: "e9d19f71-0b59-4f8e-8343-a75d30576d28"
        '400':
          description: |
            Bad Request. Possible error messages include:

            - `Invalid JSON`
            - `users: Maximum of 20 users can be processed in a single request`
            - `users: users is required`
            - `email: email is required`
            - `email: Invalid email address`
            - `The provided user attributes: "<attribute>" do not match the names of existing user attributes.`
            - `User Attribute <attribute> is type number, but passed-in value <value> is not a number.`
            - `The timezone <invalidTimezone> is not supported. Please see the connection page or [user profile page](/administration/users/your-account) for the list of valid timezones. Use e.g. "America/New_York" instead of "EST"`
            - `User Attribute <attribute> has multiple values enabled, but the passed-in value is not an array. If passing a single value for a user attribute with multiple values enabled, please wrap the value in an array [x].`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Schedule recipients
      summary: Bulk delete email-only users
      description: |
        Delete up to 100 [email-only users](/administration/users/email-only) in a single request, identified by ID, by email, or a mix of both. Deleting an email-only user also removes them as a recipient from every schedule they receive.

        If a request includes unknown or previously-deleted users, these users will be reported as `notFound` without blocking the rest of the request. 

        Use the [Delete users](/api/users/delete-user) endpoint to delete standard users and [Delete embed users](/api/users/delete-embed-user) to delete embed users.
      security:
        - bearerAuth: []
      operationId: usersDeleteEmailOnlyBulk
      requestBody:
        required: true
        content:
          application/json:
            schema:  # ApiUsersEmailOnlyUsersBulkDeleteRequest
              type: object
              properties:
                emails:
                  type: array
                  items:
                    type: string
                    format: email
                  description: Email addresses of the email-only users to delete
                  example:
                    - user@example.com
                userIds:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: IDs of the email-only users to delete. Use the [List email-only users](/api/users/list-email-only-users) endpoint to retrieve IDs.
                  example:
                    - 9e8719d9-276a-4964-9395-a493e1ba5f64
              additionalProperties: false
              anyOf:
                - properties:
                    emails:
                      minItems: 1
                  required:
                    - emails
                - properties:
                    userIds:
                      minItems: 1
                  required:
                    - userIds
            examples:
              byId:
                summary: Delete by ID
                value:
                  userIds:
                    - 9e8719d9-276a-4964-9395-a493e1ba5f64
              byEmail:
                summary: Delete by email
                value:
                  emails:
                    - user@example.com
              mixed:
                summary: Delete by a mix of IDs and emails
                value:
                  userIds:
                    - 9e8719d9-276a-4964-9395-a493e1ba5f64
                  emails:
                    - user@example.com
      responses:
        '200':
          description: |
            The email-only users that were deleted, and any identifiers that didn't match an email-only user in your organization.
          content:
            application/json:
              schema:  # ApiUsersEmailOnlyUsersBulkDeleteResponse
                type: object
                required:
                  - deleted
                  - notFound
                properties:
                  deleted:
                    type: array
                    description: The email-only users that were deleted
                    items:   # ApiEmailOnlyUserResponse
                      type: object
                      required:
                        - email
                        - userId
                      properties:
                        email:
                          type: string
                          format: email
                          description: Email address of the user
                          example: user@example.com
                        userId:
                          type: string
                          description: ID of the user
                  notFound:
                    type: object
                    required:
                      - emails
                      - userIds
                    description: Requested identifiers that didn't match an email-only user in the organization
                    properties:
                      emails:
                        type: array
                        items:
                          type: string
                        description: Requested emails that do not belong to an email-only user
                      userIds:
                        type: array
                        items:
                          type: string
                        description: Requested IDs that do not belong to an email-only user
              example:
                deleted:
                  - email: "user@example.com"
                    userId: "9e8719d9-276a-4964-9395-a493e1ba5f64"
                notFound:
                  emails:
                    - "unknown@example.com"
                  userIds: []
        '400':
          description: |
            Bad Request. Possible error messages include:

            - `Invalid JSON`
            - Must provide at least one of `emails` or `userIds`
            - `emails: Invalid email address`
            - `userIds: Invalid uuid`
            - Maximum of 100 combined emails and user IDs can be processed in a single request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Forbidden - insufficient permissions. Organization API keys are always allowed. [Personal Access Tokens (PAT)](/api/authentication#token-types) must have permissions to manage users, typically **Organization Admin**.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/user-attributes:
    get:
      tags:
        - User attributes
      summary: List user attributes
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Retrieve all [user attribute](/administration/users/attributes) definitions in your organization, including both custom-defined and system-defined attributes.

        User attributes enable row-level security filtering, personalize dashboard content, and configure per-user database credentials. This endpoint is useful for embed integrations to discover available attributes for setting values in SSO sessions.
      security:
        - bearerAuth: []
      operationId: listUserAttributes
      responses:
        '200':
          description: List of user attribute definitions
          content:
            application/json:
              schema: # UserAttributesListResponse
                type: object
                required:
                  - records
                properties:
                  records:
                    type: array
                    items:
                      type: object
                      required:
                        - default_value
                        - description
                        - id
                        - label
                        - multiple_values
                        - name
                        - system
                        - type
                      description: All user attribute definitions in the organization, including both system-defined and custom attributes
                      properties:
                        default_value:
                          $ref: '#/components/schemas/UserAttributeDefaultValue'
                          description: |
                            Default value applied when no user-specific value is set. When `multiple_values` is `true`, this is an array. Null if no default is configured.
                        description:
                          $ref: '#/components/schemas/UserAttributeDescription'
                        id:
                          $ref: '#/components/schemas/UserAttributeId'
                        label:
                          $ref: '#/components/schemas/UserAttributeLabel'
                          description: Display name shown in the Omni UI
                        multiple_values:
                          $ref: '#/components/schemas/UserAttributeMultipleValues'
                          description: Whether the attribute accepts an array of values. When `true`, `default_value` and user-specific values are arrays.
                        name:
                          $ref: '#/components/schemas/UserAttributeName'
                          description: Reference name used in model SQL and in embed SSO URL parameters
                        system:
                          $ref: '#/components/schemas/UserAttributeSystem'
                        type:
                          $ref: '#/components/schemas/UserAttributeType'
                          description: Data type that determines valid values. `String` attributes accept text, `Number` attributes accept numeric values stored as strings for precision.
              examples:
                success:
                  summary: Success response
                  value:
                    records:
                      - id: ""
                        name: "omni_user_id"
                        label: "Omni User ID"
                        type: "String"
                        multiple_values: false
                        default_value: null
                        description: null
                        system: true
                      - id: ""
                        name: "omni_user_email"
                        label: "Omni User Email"
                        type: "String"
                        multiple_values: false
                        default_value: null
                        description: null
                        system: true
                      - id: ""
                        name: "omni_user_name"
                        label: "Omni User Name"
                        type: "String"
                        multiple_values: false
                        default_value: null
                        description: null
                        system: true
                      - id: ""
                        name: "omni_user_timezone"
                        label: "Omni User Timezone"
                        type: "String"
                        multiple_values: false
                        default_value: null
                        description: null
                        system: true
                      - id: ""
                        name: "omni_is_org_admin"
                        label: "Omni Is Org Admin"
                        type: "String"
                        multiple_values: false
                        default_value: null
                        description: null
                        system: true
                      - id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                        name: "region"
                        label: "Region"
                        type: "String"
                        multiple_values: false
                        default_value: "us-east"
                        description: "User region for RLS"
                        system: false
                      - id: "b2c3d4e5-f6a7-8901-bcde-f23456789012"
                        name: "access_level"
                        label: "Access Level"
                        type: "Number"
                        multiple_values: false
                        default_value: "1"
                        description: "Numeric access level for user permissions"
                        system: false
                      - id: "c3d4e5f6-a7b8-9012-cdef-234567890123"
                        name: "departments"
                        label: "Departments"
                        type: "String"
                        multiple_values: true
                        default_value: null
                        description: "User departments for data filtering"
                        system: false
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                error: "Unauthorized"
                message: "Invalid or missing API key"
        '403':
          description: Forbidden - User does not have required admin permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                error: "Forbidden"
                message: "User does not have permission to manage user attributes"
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
   
    post:
      tags:
        - User attributes
      summary: Create user attribute
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Create a custom [user attribute](/administration/users/attributes) definition in your organization.
      security:
        - bearerAuth: []
      operationId: createUserAttribute
      requestBody:
        required: true
        content:
          application/json:
            schema: # UserAttributesCreateBody
              type: object
              required:
                - label
                - name
                - type
              additionalProperties: false
              properties:
                default_value:
                  $ref: '#/components/schemas/UserAttributeDefaultValue'
                description:
                  $ref: '#/components/schemas/UserAttributeDescription'
                label:
                  $ref: '#/components/schemas/UserAttributeLabel'
                multiple_values:
                  $ref: '#/components/schemas/UserAttributeMultipleValues'
                name:
                  $ref: '#/components/schemas/UserAttributeName'
                type:
                  $ref: '#/components/schemas/UserAttributeType'
            example:
              name: "region"
              label: "Region"
              type: "String"
              multiple_values: false
              default_value: "us-east"
              description: "User region for row-level security filtering"
      responses:
        '201':
          description: User attribute definition created
          content:
            application/json:
              schema: # UserAttributesCreateResponse
                $ref: '#/components/schemas/UserAttributeObject'
              example:
                id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                name: "region"
                label: "Region"
                type: "String"
                multiple_values: false
                default_value: "us-east"
                description: "User region for row-level security filtering"
                system: false
        '400':
          description: |
            Bad Request. The body does not parse or fails schema validation. `detail` names the invalid field or carries the value validation message, for example:

            - `name: Required`
            - `name: Must start with a letter and contain only letters, numbers, and underscores`
            - `label: Required`
            - `type: Invalid option: expected one of "String"|"Number"`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: name: Required"
                status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '403':
          description: |
            Forbidden. Requires Manage User Attributes permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "You do not have permission to manage user attributes"
                status: 403
        '409':
          description: |
            Conflict. A user attribute definition with this name or label already exists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
              example:
                detail: "Attribute already exists with name \"region\" and label \"Region\""
                status: 409
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/user-attributes/{userAttributeId}:
    put:
      tags:
        - User attributes
      summary: Update a user attribute definition
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Update a custom [user attribute](/administration/users/attributes) definition. Omitted fields keep their current values. The attribute's `type` cannot change, and a multiple-values attribute cannot be changed back to a single value.
      operationId: userAttributesUpdate
      parameters:
        - $ref: '#/components/parameters/userAttributeId'
      requestBody:
        required: true
        content:
          application/json:
            schema: # UserAttributesUpdateBody
              $ref: '#/components/schemas/UserAttributeObject'
            example:
              label: Region
              name: region
              default_value: us-east
              description: User region for row-level security filtering
              multiple_values: false
      responses:
        '200':
          description: User attribute definition updated
          content:
            application/json:
              schema: # UserAttributesUpdateResponse
                type: object
                required:
                  - default_value
                  - description
                  - id
                  - label
                  - multiple_values
                  - name
                  - system
                  - type
                properties:
                  default_value:
                    $ref: '#/components/schemas/UserAttributeDefaultValue'
                    description: |
                      Default value applied when no user-specific value is set. When `multiple_values` is `true`, this is an array. Null if no default is configured.
                  description:
                    $ref: '#/components/schemas/UserAttributeDescription'
                  id:
                    $ref: '#/components/schemas/UserAttributeId'
                  label:
                    $ref: '#/components/schemas/UserAttributeLabel'
                    description: Display name shown in the Omni UI
                  multiple_values:
                    $ref: '#/components/schemas/UserAttributeMultipleValues'
                    description: |
                      Whether the attribute accepts an array of values. When `true`, `default_value` and user-specific values are arrays.
                  name:
                    $ref: '#/components/schemas/UserAttributeName'
                    description: |
                      Reference name used in model SQL and in embed SSO URL parameters
                  system:
                    $ref: '#/components/schemas/UserAttributeSystem'
                  type:
                    $ref: '#/components/schemas/UserAttributeType'
                    description: |
                      Data type that determines valid values. `String` attributes accept text, `Number` attributes accept numeric values stored as strings for precision.
              example:
                id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                name: region
                label: Region
                type: String
                multiple_values: false
                default_value: us-east
                description: User region for row-level security filtering
                system: false
        '400':
          description: |
            Bad Request. The request body or the `userAttributeId` parameter failed validation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: id: Invalid UUID"
                status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '403':
          description: |
            Forbidden. Requires Organization Admin permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "You do not have permission to manage user attributes"
                status: 403
        '404':
          description: |
            Not Found. No user attribute definition with that ID exists in the caller's organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "User attribute definition does not exist"
                status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          description: |
            Conflict. Another user attribute definition already has this name or label, or the definition changed while it was being updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
              example:
                detail: "A user attribute named \"region\" already exists"
                status: 409
        '429':
          $ref: '#/components/responses/TooManyRequests'
    
    delete:
      tags:
        - User attributes
      summary: Delete a user attribute definition
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Delete a custom [user attribute](/administration/users/attributes) definition. This permanently removes the value every user holds for the attribute.

        Additionally, any references to the attribute will break upont deletion. This includes model SQL, dashboard or embed SSO URLs that pass it as a parameter, and connections that select an environment by its value will fall back to the default environment instead.
      operationId: userAttributesDelete
      parameters:
        - $ref: '#/components/parameters/userAttributeId'
      responses:
        '204':
          description: User attribute definition deleted
        '400':
          description: |
            Bad Request. The `userAttributeId` parameter is not a valid UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: id: Invalid UUID"
                status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '403':
          description: |
            Forbidden. Requires Organization Admin permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "You do not have permission to manage user attributes"
                status: 403
        '404':
          description: |
            Not Found. No user attribute definition with that ID exists in the caller's organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "User attribute definition does not exist"
                status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/user-attributes/{userAttributeId}/selectable-values:
    get:
      tags:
        - User attributes
      summary: Retrieve switchable values for a user or group
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Retrieve the values a single user or group can switch a [user attribute](/administration/users/attributes/user-selected) to.

        This endpoint returns only the subject's own provisioning. It does not include values a user inherits from their groups, and it does not reflect the ability of **Organization Admin** users to switch any attribute to any value.
      security:
        - bearerAuth: []
      operationId: userAttributeSelectableValuesGet
      parameters:
        - $ref: '#/components/parameters/userAttributeId'
        - $ref: '#/components/parameters/userId'
          description: The user's membership ID, not their user ID. Mutually exclusive with `groupId`.
        - name: groupId
          in: query
          required: false
          description: |
            The user group's mini UUID (short ID), matching the ID returned by the [List user groups](/api/user-groups/list-user-groups) endpoint. Mutually exclusive with `userId`.
          schema:
            type: string
          example: X9kQ2mPz
      responses:
        '200':
          description: |
            The subject's provisioned values for this attribute.

            **Note**: A user or group with no provisioned values returns `200` with `values: []` and `allow_any_value: false`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SelectableValuesResponse'
              examples:
                provisioned:
                  summary: User with provisioned values
                  value:
                    user_attribute_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                    values:
                      - acme
                      - globex
                    allow_any_value: false
                none:
                  summary: User with no provisioning
                  value:
                    user_attribute_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                    values: []
                    allow_any_value: false
        '400':
          description: |
            Bad Request. Both or neither of `userId` and `groupId` were provided, or `userId` is not a valid UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Supply exactly one of userId or groupId"
                status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '403':
          description: |
            Forbidden. Requires Organization Admin permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "You do not have permission to manage user attributes"
                status: 403
        '404':
          description: |
            Not Found. The user attribute definition, user, or group does not exist in the caller's organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "User attribute definition does not exist"
                status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

    put:
      tags:
        - User attributes
      summary: Set switchable values for a user or group
      description: |
        <Note>
          **Organization Admin** permissions are required to use this endpoint.
        </Note>

        Replaces the values a user or group may switch this attribute to. Values are validated against the attribute type, such as `String`.

        Provide one of `user_id` or `group_id`; providing both will result in an error.

        <Warning>
          Provisioning a value grants data access. Shared extensions and access filters often route on the same attribute, so a switchable value typically lets the user read another tenant's rows. Provision only the values a user or group should see.
        </Warning>
      x-mint:
        content: |
          ## Value requirements

          - Values must be valid for the attribute's type; for example, a `Number` attribute rejects `acme`
          - Requests can contain up to 300 values of up to 512 characters each. For a larger set, use `allow_any_value` instead. 

          ## Removing provisioning

          Sending `values: []` with `allow_any_value: false` removes the provisioning entirely. Any switch the user already made no longer applies and they fall back to their assigned value.
      security:
        - bearerAuth: []
      operationId: userAttributeSelectableValuesUpdate
      parameters:
        - $ref: '#/components/parameters/userAttributeId'
      requestBody:
        required: true
        content:
          application/json:
            schema: # SelectableValuesUpdateBody
              anyOf:
                - type: object
                  title: For a user
                  required:
                    - user_id
                    - values
                  additionalProperties: false
                  properties:
                    user_id:
                      type: string
                      format: uuid
                      description: The user's membership ID, not their user ID. Mutually exclusive with `group_id`.
                      example: 8f14e45f-ceea-467a-9c2b-8f1d2a3b4c5d
                    values:
                      $ref: '#/components/schemas/UserAttributeSwitchValues'
                    allow_any_value:
                      $ref: '#/components/schemas/UserAttributeAllowAnyValue'
                      description: |
                        Whether the user can switch to a value that isn't listed.
                - type: object
                  title: For a group
                  required:
                    - group_id
                    - values
                  additionalProperties: false
                  properties:
                    group_id:
                      type: string
                      description: The user group's mini UUID (short ID), matching the ID returned by the [List user groups](/api/user-groups/list-user-groups) endpoint. Mutually exclusive with `user_id`.
                      example: X9kQ2mPz
                    values:
                      $ref: '#/components/schemas/UserAttributeSwitchValues'
                    allow_any_value:
                      $ref: '#/components/schemas/UserAttributeAllowAnyValue'
            examples:
              user:
                summary: Provision two values for a user
                value:
                  user_id: 8f14e45f-ceea-467a-9c2b-8f1d2a3b4c5d
                  values:
                    - acme
                    - globex
              groupAny:
                summary: Let a group switch to any value
                value:
                  group_id: X9kQ2mPz
                  values: []
                  allow_any_value: true
              revoke:
                summary: Remove a user's provisioning
                value:
                  user_id: 8f14e45f-ceea-467a-9c2b-8f1d2a3b4c5d
                  values: []
      responses:
        '200':
          description: The provisioning as stored
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SelectableValuesResponse'
              examples:
                user:
                  summary: Two values provisioned for a user
                  value:
                    user_attribute_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                    values:
                      - acme
                      - globex
                    allow_any_value: false
                groupAny:
                  summary: Group allowed any value
                  value:
                    user_attribute_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                    values: []
                    allow_any_value: true
        '400':
          description: |
            Bad Request. Both or neither of `user_id` and `group_id` were provided, the body failed validation, or a value is not valid for the attribute's type.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              examples:
                subject:
                  summary: Both subjects provided
                  value:
                    detail: "Supply exactly one of user_id or group_id"
                    status: 400
                type:
                  summary: Value rejected by the attribute type
                  value:
                    detail: "Invalid allowed values for \"Region code\": \"acme\" is not a valid number"
                    status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '403':
          description: |
            Forbidden. Requires Organization Admin permissions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
              example:
                detail: "You do not have permission to manage user attributes"
                status: 403
        '404':
          description: |
            Not Found. The user attribute definition, user, or group does not exist in the caller's organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "User attribute definition does not exist"
                status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/user-attribute-selections:
    get:
      tags:
        - User attributes
      summary: List switchable attributes for the current user
      description: |
        Retrieve the [user attributes](/administration/users/attributes/user-selected) the calling user can switch, along with any switch that is currently active.

        If the calling user is using a PAT, the token will act as the authenticating user. An Organization API key will act as the key's creator.
      security:
        - bearerAuth: []
      operationId: userAttributeSelectionsList
      responses:
        '200':
          description: |
            The caller's switchable attributes and active switches.

            If nothing is provisioned for the calling user, the `records` in the response will be empty.
          
            An **Organization Admin** will get a record for every attribute with `allow_any_value: true`, whether or not anything is provisioned for them.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserAttributeSelectionsResponse'
              examples:
                success:
                  summary: One switchable attribute, no active switch
                  value:
                    records:
                      - user_attribute_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                        name: client
                        label: Client
                        multiple_values: false
                        allow_any_value: false
                        selectable_values:
                          - acme
                          - globex
                          - initech
                        selected_values: null
                empty:
                  summary: Nothing provisioned for the caller
                  value:
                    records: []
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

    put:
      tags:
        - User attributes
      summary: Switch an attribute value for the current user
      description: |
        Switches a user attribute value for the calling user. The switch then drives dynamic shared extensions, access filters and connection environment routing exactly as an assigned value would.

        To change what values a user can switch to, use the [Set switchable values](/api/user-attributes/set-switchable-values-for-a-user-or-group) endpoint instead.
      security:
        - bearerAuth: []
      operationId: userAttributeSelectionUpdate
      x-mint:
        content: |
          ## Allowed values

          The calling user can only successfully switch if values are already provisioned, either by listing them in `selectable_values` or enabling `allow_any_value`. 
          
          A single-valued attribute accepts exactly one value. A multi-valued attribute accepts any subset of up to 300 values. 
      requestBody:
        required: true
        content:
          application/json:
            schema: # UserAttributeSelectionUpdateBody
              type: object
              required:
                - user_attribute_id
                - values
              additionalProperties: false
              properties:
                user_attribute_id:
                  $ref: '#/components/schemas/UserAttributeId'
                  description: The user attribute definition to switch.
                values:
                  $ref: '#/components/schemas/UserAttributeSwitchValues'
            example:
              user_attribute_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
              values:
                - globex
      responses:
        '200':
          description: The caller's switchable attributes after the change
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserAttributeSelectionsResponse'
              example:
                records:
                  - user_attribute_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                    name: client
                    label: Client
                    multiple_values: false
                    allow_any_value: false
                    selectable_values:
                      - acme
                      - globex
                      - initech
                    selected_values:
                      - globex
        '400':
          description: |
            Bad Request. Possible caues:
            
            - A value is not provisioned for the caller
            - More than one value was sent for a single-valued attribute
            - A value is not valid for the attribute's type or exceeds 512 characters under `allow_any_value`
            - No non-blank value was sent, or more than 300 values were sent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              examples:
                notProvisioned:
                  summary: Value not provisioned for the caller
                  value:
                    detail: "Value not available for \"Client\": someone-elses-client"
                    status: 400
                single:
                  summary: Two values on a single-valued attribute
                  value:
                    detail: "\"Client\" accepts a single value"
                    status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '404':
          description: |
            Not Found. The attribute is not switchable for the calling user because nothing is provisioned for them.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: "User attribute is not switchable"
                status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

    delete:
      tags:
        - User attributes
      summary: Clear a switched attribute value for the current user
      description: |
        Clear the calling user's switch for a [user attribute](/administration/users/attributes/user-selected), restoring the administrator-assigned value. Clearing an attribute that is not currently switched succeeds and changes nothing.
      security:
        - bearerAuth: []
      operationId: userAttributeSelectionDelete
      parameters:
        - name: userAttributeId
          in: query
          required: true
          description: User attribute ID
          schema:
            type: string
            format: uuid
            example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
      responses:
        '200':
          description: The caller's switchable attributes after the change
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserAttributeSelectionsResponse'
              example:
                records:
                  - user_attribute_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
                    name: client
                    label: Client
                    multiple_values: false
                    allow_any_value: false
                    selectable_values:
                      - acme
                      - globex
                      - initech
                    selected_values: null
        '400':
          description: |
            Bad Request. The `userAttributeId` parameter is missing or is not a valid UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
              example:
                detail: "Bad Request: userAttributeId: Invalid UUID"
                status: 400
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
              example:
                detail: "Unauthorized: Missing or invalid API key"
                status: 401
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /scim/v2/users:
    post:
      tags:
        - Users
      summary: Create user
      description: Create a user.
      x-mint:
        content: |
          <Tip>
            To manage model and connection role assignments for users, see the [User model role APIs](/api/user-model-roles).
          </Tip>
      security:
        - orgApiKey: []
      operationId: createUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - displayName
                - userName
              properties:
                displayName:
                  type: string
                  description: The user's display name
                  example: Blob Ross
                userName:
                  type: string
                  format: email
                  description: The user's email address
                  example: blob.ross@blobsrus.com
                urn:omni:params:1.0:UserAttribute:
                  type: object
                  description: |
                    [User attributes](/administration/users/attributes) as key/value pairs, where keys map to the IDs of user attributes defined in Omni. This is the **Reference** column in the **User attributes** page.
                  example:
                    good_blob: true
                urn:omni:params:scim:schemas:extension:user:2.0:
                  type: object
                  description: Omni-specific user metadata.
                  properties:
                    userType:
                      type: string
                      enum: ['Admin','Member','External']
                      description: |
                        The user's organization role: 
                        
                        - `Admin` - Organization Admin. `Admin` is refused for external users.
                        - `Member` - Standard user. Without a recognized value, the user is created as a `Member`.
                        - `External` - External user
                      example: Member
                    emailLinkSignIn:
                      type: boolean
                      description: |
                        Whether this user can sign in with an [email link](/administration/users/settings#email-link-sign-in) while the organization requires SSO.
                      example: true
      responses:
        '201':
          description: User created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimUser'
              example:
                active: true
                displayName: Blob Ross
                emails:
                  - primary: true
                    value: blob.ross@blobsrus.co
                groups: []
                id: 9e8719d9-276a-4964-9395-a493189a247c
                meta:
                  created: "2024-12-03T23:13:14.109Z"
                  lastModified: "2024-12-03T23:13:14.109Z"
                  resourceType: User
                schemas:
                  - "urn:ietf:params:scim:schemas:core:2.0:User"
                userName: blob.ross@blobsrus.co
                urn:omni:params:1.0:UserAttribute:
                  good_blob: "yes"
                urn:omni:params:scim:schemas:extension:user:2.0:
                  lastLogin: null
                  userType: Member
                  emailLinkSignIn: false
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - Users
      summary: List users
      description: |
        List users in the organization, sorted by creation time. Use the [embed users](/api/users/list-embed-users) endpoint to retrieve embed users.
      security:
        - orgApiKey: []
      operationId: listUsers
      parameters:
        - name: filter
          in: query
          schema:
            type: string
          description: |
            Filter users using the `userName` field with SCIM operators:

            | Operator | Description | Example |
            |----------|-------------|---------|
            | `eq`     | Exact match | `userName eq "blob.ross@blobsrus.co"` |
            | `co`     | Contains substring | `userName co "smith"` |

            **Case sensitivity:**

            - Attribute names (`userName`, `USERNAME`) are case-insensitive
            - Operator names (`eq`, `EQ`, `co`, `CO`) are case-insensitive
            - Email comparisons are case-insensitive

            Usernames should be URL-encoded when passed as filters. For example, `userName eq "user@example.com"` will become `userName%20eq%20%22user%40example.com%22` when encoded.
          example: userName co "smith"
        - name: count
          in: query
          schema:
            type: integer
            default: 100
          description: The number of users to return per page
          example: 50
        - name: startIndex
          in: query
          schema:
            type: integer
            default: 1
          description: An integer index that determines the starting point of the sorted result list
          example: 1
      responses:
        '200':
          description: List of users
          content:
            application/json:
              schema:
                type: object
                properties:
                  Resources:
                    type: array
                    description: The list of users
                    items:
                      $ref: '#/components/schemas/ScimUser'
                  schemas:
                    type: array
                    description: SCIM schema information for the response
                    items:
                      type: string
                  itemsPerPage:
                    type: integer
                    description: The number of users returned in the current page
                  totalResults:
                    type: integer
                    description: The total number of users matching the query
                  startIndex:
                    type: integer
                    description: The starting index of the current page in the result set
              example:
                Resources:
                  - active: true
                    displayName: Blob Ross
                    emails:
                      - primary: true
                        value: blob.ross@blobsrus.co
                    groups: []
                    id: 9e8719d9-276a-4964-9395-a493189a247c
                    meta:
                      created: "2024-11-04T16:01:47.015Z"
                      lastModified: "2024-11-04T16:05:45.356Z"
                      resourceType: User
                    schemas:
                      - "urn:ietf:params:scim:schemas:core:2.0:User"
                      - "urn:omni:params:1.0:UserAttribute"
                      - "urn:omni:params:scim:schemas:extension:user:2.0"
                    userName: blob.ross@blobsrus.co
                    urn:omni:params:1.0:UserAttribute:
                      good_blob: "yes"
                    urn:omni:params:scim:schemas:extension:user:2.0:
                      lastLogin: "2024-01-03T00:00:00.000Z"
                      userType: Member
                      emailLinkSignIn: false
                itemsPerPage: 1
                schemas:
                  - "urn:ietf:params:scim:api:messages:2.0:ListResponse"
                startIndex: 1
                totalResults: 1
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /scim/v2/users/{userId}:
    get:
      tags:
        - Users
      summary: Retrieve user
      description: Retrieve a user using their unique ID.
      security:
        - orgApiKey: []
      operationId: getUser
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the user to be retrieved
          example: 9e8719d9-276a-4964-9395-a493189a247c
      responses:
        '200':
          description: User details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimUser'
              example:
                active: true
                displayName: Blob Ross
                emails:
                  - primary: true
                    value: blob.ross@blobsrus.co
                groups: []
                id: 9e8719d9-276a-4964-9395-a493189a247c
                meta:
                  created: "2024-12-03T23:13:14.109Z"
                  lastModified: "2024-12-03T23:13:14.109Z"
                  resourceType: User
                schemas:
                  - "urn:ietf:params:scim:schemas:core:2.0:User"
                  - "urn:omni:params:1.0:UserAttribute"
                  - "urn:omni:params:scim:schemas:extension:user:2.0"
                userName: blob.ross@blobsrus.co
                urn:omni:params:1.0:UserAttribute:
                  good_blob: "yes"
                urn:omni:params:scim:schemas:extension:user:2.0:
                  lastLogin: "2024-01-03T00:00:00.000Z"
                  userType: Member
                  emailLinkSignIn: false
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - Users
      summary: Replace user
      description: |
        Replace the specified user. Per the SCIM 2.0 specification, `PUT` replaces the entire user resource: any optional attribute omitted from the request body is reset to its default value.

        To update individual attributes without affecting others, use the [Update user](/api/users/update-user) endpoint, which performs a partial update via `PATCH`.
      security:
        - orgApiKey: []
      operationId: replaceUser
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the user to be replaced
          example: 9e8719d9-276a-4964-9395-a493189a247c
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userName
              properties:
                active:
                  type: boolean
                  default: true
                  description: Whether the user is active. Set to `false` to deactivate the user.
                  example: true
                displayName:
                  type: string
                  description: The user's display name
                  example: Blob Ross
                userName:
                  type: string
                  format: email
                  description: The user's email address.
                  example: blob.ross@blobsrus.co
                urn:omni:params:1.0:UserAttribute:
                  type: object
                  description: |
                    [User attributes](/administration/users/attributes) as key/value pairs, where keys map to the IDs of user attributes defined in Omni. This is the **Reference** column in the **User attributes** page.
                  example:
                    good_blob: "yes"
                urn:omni:params:scim:schemas:extension:user:2.0:
                  type: object
                  description: Omni-specific user metadata.
                  properties:
                    userType:
                      type: string
                      description: |
                        The user's organization role: 
                        
                        - `Admin` - Organization Admin. `Admin` is refused for external users.
                        - `Member` - Standard user. Without a recognized value, the user is created as a `Member`.
                        - `External` - External user
                      example: Member
                    emailLinkSignIn:
                      type: boolean
                      description: |
                        Whether this user can sign in with an [email link](/administration/users/settings#email-link-sign-in) while the organization requires SSO.

                        Unlike other optional fields on this endpoint, omitting `emailLinkSignIn` or sending `null` keeps its current value instead of resetting it. Identity providers send `null` for attributes they don't map, so this keeps an admin-set exception from being undone by a sync.  
                      example: true
      responses:
        '200':
          description: User replaced successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimUser'
              example:
                active: true
                displayName: Blob Ross
                emails:
                  - primary: true
                    value: blob.ross@blobsrus.co
                groups: []
                id: 9e8719d9-276a-4964-9395-a493189a247c
                meta:
                  created: "2024-12-03T23:13:14.109Z"
                  lastModified: "2024-12-03T23:13:14.109Z"
                  resourceType: User
                schemas:
                  - "urn:ietf:params:scim:schemas:core:2.0:User"
                userName: blob.ross@blobsrus.co
                urn:omni:params:1.0:UserAttribute:
                  good_blob: "yes"
                urn:omni:params:scim:schemas:extension:user:2.0:
                  lastLogin: "2024-01-03T00:00:00.000Z"
                  userType: Member
                  emailLinkSignIn: false
        '400':
          description: Invalid request body
        '429':
          $ref: '#/components/responses/TooManyRequests'
    patch:
      tags:
        - Users
      summary: Update user
      description: |
        Update the specified user by applying a list of [SCIM 2.0 patch operations](https://datatracker.ietf.org/doc/html/rfc7644#section-3.5.2). Attributes not referenced in the request are left unchanged.

        To replace the entire user resource in a single request, use the [Replace user](/api/users/replace-user) endpoint.

        <Warning>
          Omni does not have a reversible "deactivated" state for users. Setting `active` to `false` revokes the user's membership, which is **not reversible**: their schedules are deleted (unless transferred first) and their Personal Access Tokens are disabled. See [Revoking user memberships](/administration/users/delete) before using this operation.
        </Warning>
      security:
        - orgApiKey: []
      operationId: updateUser
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the user to be updated
          example: 9e8719d9-276a-4964-9395-a493189a247c
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - Operations
                - schemas
              properties:
                schemas:
                  type: array
                  description: The SCIM schema URIs for the request. Must include `urn:ietf:params:scim:api:messages:2.0:PatchOp`.
                  items:
                    type: string
                    enum:
                      - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                  example:
                    - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                Operations:
                  type: array
                  minItems: 1
                  description: The list of patch operations to apply to the user.
                  items:
                    type: object
                    required:
                      - op
                      - value
                    properties:
                      op:
                        type: string
                        description: The operation to perform.
                        enum:
                          - add
                          - replace
                          - remove
                      path:
                        type: string
                        description: |
                          The attribute path the operation targets. Omit to apply the operation to the resource as a whole. 
                          
                          The Omni user extension's attributes, such as `emailLinkSignIn` and `userType`, can be targeted with a namespaced path (`urn:omni:params:scim:schemas:extension:user:2.0:emailLinkSignIn`) or left out and nested in `value` instead.
                        example: displayName
                      value:
                        description: The value to apply for the operation. Required for `add` and `replace` operations.
                        example: "Blob Ross"
                        anyOf:
                          - type: string
                          - type: number
                          - type: boolean
                          - type: array
                            items:
                              type: string
                          - type: object
                            properties:
                              active:
                                type: boolean
                              displayName:
                                type: string
                              userName:
                                type: string
                                format: email
                              urn:omni:params:scim:schemas:extension:user:2.0:
                                type: object
                                properties:
                                  userType:
                                    type: string
                                  emailLinkSignIn:
                                    type: boolean
            examples:
              revokeUser:
                summary: Revoke a user's membership
                value:
                  schemas:
                    - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                  Operations:
                    - op: replace
                      path: active
                      value: false
              updateDisplayNameAndEmail:
                summary: Update display name and email
                value:
                  schemas:
                    - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                  Operations:
                    - op: replace
                      value:
                        displayName: Blob Ross
                        userName: blob.ross@blobsrus.co
              setEmailLinkSignIn:
                summary: Let the user sign in with an email link
                value:
                  schemas:
                    - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                  Operations:
                    - op: replace
                      path: "urn:omni:params:scim:schemas:extension:user:2.0:emailLinkSignIn"
                      value: true
      responses:
        '200':
          description: User updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimUser'
              example:
                active: false
                displayName: Blob Ross
                emails:
                  - primary: true
                    value: blob.ross@blobsrus.co
                groups: []
                id: 9e8719d9-276a-4964-9395-a493189a247c
                meta:
                  created: "2024-12-03T23:13:14.109Z"
                  lastModified: "2024-12-03T23:13:14.109Z"
                  resourceType: User
                schemas:
                  - "urn:ietf:params:scim:schemas:core:2.0:User"
                userName: blob.ross@blobsrus.co
        '400':
          description: Invalid patch operations
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Users
      summary: Delete user
      description: |
        Delete the specified user. Use the [Delete embed user](/api/users/delete-embed-user) endpoint to delete embed users.

        <Warning>
          Deleting a user revokes their membership, which is **not reversible**: their schedules are deleted (unless transferred first) and their Personal Access Tokens are disabled. See [Revoking user memberships](/administration/users/delete) for the full list of consequences before using this operation.
        </Warning>
      security:
        - orgApiKey: []
      operationId: deleteUser
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the user to be deleted
          example: 9e8719d9-276a-4964-9395-a493189a247c
      responses:
        '204':
          description: User deleted successfully
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /scim/v2/embed/users:
    get:
      tags:
        - Users
      summary: List embed users
      description: |
        List embed users in the organization, sorted by creation time. Use the [users](/api/users/list-users) endpoint to retrieve standard users.
      security:
        - orgApiKey: []
      operationId: listEmbeddedUsers
      parameters:
        - name: filter
          in: query
          schema:
            type: string
          description: |
            Filter embed users using the `userName` or `embedExternalId` fields with SCIM operators.

            | Operator | Description | Example |
            |----------|-------------|---------|
            | `eq`     | Exact match | `userName eq "blob.ross@blobsrus.co"` |
            | `co`     | Contains substring | `embedExternalId co "sales"` |

            **Case sensitivity:**

            - Attribute names (`userName`, `embedExternalId`) are case-insensitive
            - Operator names (`eq`, `EQ`, `co`, `CO`) are case-insensitive
            - Email comparisons are case-insensitive
            - External ID (`embedExternalId`) comparisons are case-sensitive, per SCIM spec

            Usernames should be URL-encoded when passed as filters. For example, `userName eq "user@example.com"` will become `userName%20eq%20%22user%40example.com%22` when encoded.
          example: embedExternalId co "sales"
        - name: count
          in: query
          schema:
            type: integer
            default: 100
          description: The number of users to return
          example: 50
        - name: startIndex
          in: query
          schema:
            type: integer
            default: 1
          description: An integer index that determines the starting point of the sorted result list
          example: 1
      responses:
        '200':
          description: of embed users
          content:
            application/json:
              schema:
                type: object
                properties:
                  Resources:
                    type: array
                    description: The list of embed users
                    items:
                      $ref: '#/components/schemas/ScimEmbedUser'
                  schemas:
                    type: array
                    description: SCIM schema information for the response
                    items:
                      type: string
                  itemsPerPage:
                    type: integer
                    description: The number of embed users returned in the current page
                  totalResults:
                    type: integer
                    description: The total number of embed users matching the query
                  startIndex:
                    type: integer
                    description: The starting index of the current page in the result set
              example:
                Resources:
                  - active: true
                    displayName: Blobby
                    emails:
                      - primary: true
                        value: embed-user-i_4TrNwyVTu34bZaC35VJVD3-pknp4YF7V8_Bi4TMdw@blobsrus.embed-exploreomni.co
                    groups:
                      - display: All Embed Users
                        value: 4GcvQ2D9
                      - display: Omni
                        value: nqGuU_uh
                    id: 2212aecf-a2ba-4d99-b23b-f615bc4c6522
                    meta:
                      created: "2024-09-30T20:25:01.822Z"
                      lastModified: "2024-09-30T20:51:47.558Z"
                      resourceType: User
                    schemas:
                      - "urn:ietf:params:scim:schemas:core:2.0:User"
                    userName: embed-user-i_4TrNwyVTu34bZaC35VJVD3-pknp4YF7V8_Bi4TMdw@blobsrus.embed-exploreomni.co
                    embedEmail: null
                    embedEntity: omni
                    embedExternalId: blobby-manager
                itemsPerPage: 1
                schemas:
                  - "urn:ietf:params:scim:api:messages:2.0:ListResponse"
                startIndex: 1
                totalResults: 1
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /scim/v2/embed/users/{userId}:
    get:
      tags:
        - Users
      summary: Retrieve embed user
      description: Retrieve an embed user using their unique ID. Use the [Retrieve user](/api/users/retrieve-user) endpoint to retrieve standard users.
      security:
        - orgApiKey: []
      operationId: getEmbeddedUser
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the embed user to be retrieved
          example: 2212aecf-a2ba-4d99-b23b-f615bc4c6522
      responses:
        '200':
          description: Embed user details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimEmbedUser'
              example:
                active: true
                displayName: Blobby
                emails:
                  - primary: true
                    value: embed-user-i_4TrNwyVTu34bZaC35VJVD3-pknp4YF7V8_Bi4TMdw@blobsrus.embed-exploreomni.co
                groups:
                  - display: All Embed Users
                    value: 4GcvQ2D9
                  - display: Omni
                    value: nqGuU_uh
                id: 2212aecf-a2ba-4d99-b23b-f615bc4c6522
                meta:
                  created: "2024-09-30T20:25:01.822Z"
                  lastModified: "2024-09-30T20:51:47.558Z"
                  resourceType: User
                schemas:
                  - "urn:ietf:params:scim:schemas:core:2.0:User"
                userName: embed-user-i_4TrNwyVTu34bZaC35VJVD3-pknp4YF7V8_Bi4TMdw@blobsrus.embed-exploreomni.co
                embedEmail: null
                embedEntity: omni
                embedExternalId: blobby-manager
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - Users
      summary: Delete embed user
      description: Delete the specified embed user. Use the [Delete user](/api/users/delete-user) endpoint to delete standard users.
      security:
        - orgApiKey: []
      operationId: deleteEmbeddedUser
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the embed user to be deleted
          example: 2212aecf-a2ba-4d99-b23b-f615bc4c6522
      responses:
        '204':
          description: Embed user deleted successfully
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /scim/v2/groups:
    post:
      tags:
        - User groups
      summary: Create user group
      description: |
        Create a [user group](/administration/users/groups). To manage model and connection role assignments for user groups, see the [User group model role APIs](/api/user-group-model-roles).
      security:
        - orgApiKey: []
      operationId: createUserGroup
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - displayName
              properties:
                displayName:
                  type: string
                  description: The name of the group. Names must be 64 characters or less.
                  example: Blob Sales
                members:
                  type: array
                  description: A list of users to add as group members. Each member should be specified as an object containing a user ID.
                  items:
                    type: object
                    properties:
                      value:
                        type: string
                        description: The user's unique identifier
                  example:
                    - value: 9e8719d9-276a-4964-9395-a493189a247c
      responses:
        '201':
          description: Group created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimGroup'
              example:
                displayName: Blob Sales
                id: mEhXj6ZI
                meta:
                  created: "2024-12-04T00:08:03.250Z"
                  lastModified: "2024-12-04T00:08:03.250Z"
                  resourceType: Group
                schemas:
                  - "urn:ietf:params:scim:schemas:core:2.0:Group"
                members:
                  - display: blob.ross@blobsrus.co
                    value: 9e8719d9-276a-4964-9395-a493189a247c
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - User groups
      summary: List user groups
      description: Return a list of user groups, sorted by creation time.
      security:
        - orgApiKey: []
      operationId: listUserGroups
      parameters:
        - name: count
          in: query
          schema:
            type: integer
            default: 100
          description: The number of groups to return
          example: 50
        - name: excludedAttributes
          in: query
          schema:
            type: string
            enum:
              - members
          description: Attributes to exclude from the response.
          example: members
        - name: startIndex
          in: query
          schema:
            type: integer
            default: 1
          description: An integer index that determines the starting point of the sorted result list
          example: 1
      responses:
        '200':
          description: List of user groups
          content:
            application/json:
              schema:
                type: object
                properties:
                  Resources:
                    type: array
                    description: The list of user groups
                    items:
                      $ref: '#/components/schemas/ScimGroup'
                  schemas:
                    type: array
                    description: SCIM schema information for the response
                    items:
                      type: string
                  itemsPerPage:
                    type: integer
                    description: The number of groups returned in the current page
                  startIndex:
                    type: integer
                    description: The starting index of the current page in the result set
                  totalResults:
                    type: integer
                    description: The total number of groups matching the query
              example:
                Resources:
                  - displayName: Blob Sales
                    id: mEhXj6ZI
                    meta:
                      created: "2024-08-29T20:33:36.626Z"
                      lastModified: "2024-08-29T20:33:36.626Z"
                      resourceType: Group
                    schemas:
                      - "urn:ietf:params:scim:schemas:core:2.0:Group"
                    members:
                      - display: blob.ross@blobsrus.co
                        value: 9e8719d9-276a-4964-9395-a493189a247c
                itemsPerPage: 1
                schemas:
                  - "urn:ietf:params:scim:api:messages:2.0:ListResponse"
                startIndex: 1
                totalResults: 1
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /scim/v2/groups/{userGroupId}:
    get:
      tags:
        - User groups
      summary: Retrieve user group
      description: Retrieves a user group using its unique ID.
      security:
        - orgApiKey: []
      operationId: getUserGroup
      parameters:
        - name: userGroupId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the group to be retrieved
          example: mEhXj6ZI
        - name: excludedAttributes
          in: query
          schema:
            type: string
            enum:
              - members
          description: Attributes to exclude from the response.
          example: members
      responses:
        '200':
          description: User group details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimGroup'
              example:
                displayName: Blob Sales
                id: mEhXj6ZI
                meta:
                  created: "2024-08-29T20:33:36.626Z"
                  lastModified: "2024-08-29T20:33:36.626Z"
                  resourceType: Group
                schemas:
                  - "urn:ietf:params:scim:schemas:core:2.0:Group"
                members:
                  - display: blob.ross@blobsrus.co
                    value: 9e8719d9-276a-4964-9395-a493189a247c
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      tags:
        - User groups
      summary: Replace user group
      description: |
        Replace the specified user group. Per the SCIM 2.0 specification, `PUT` replaces the entire group resource, including its membership.

        To add or remove individual members without replacing the full membership list, use the [Update user group](/api/user-groups/update-user-group) endpoint, which performs a partial update via `PATCH`.
      security:
        - orgApiKey: []
      operationId: replaceUserGroup
      parameters:
        - name: userGroupId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the group to be replaced
          example: mEhXj6ZI
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - displayName
                - members
              properties:
                displayName:
                  type: string
                  description: The name of the group. Names must be 64 characters or less.
                  example: Blob SEs
                members:
                  type: array
                  description: |
                    A list of users that defines the group membership. **Note**: This will overwrite the existing membership. Include existing members to retain their membership.
                  items:
                    type: object
                    properties:
                      display:
                        type: string
                        description: The user's email address
                      value:
                        type: string
                        description: The user's unique identifier
                  example:
                    - display: blob.ross@blobsrus.co
                      value: 9e8719d9-276a-4964-9395-a493189a247c
      responses:
        '200':
          description: Group replaced successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimGroup'
              example:
                displayName: Blob SEs
                id: mEhXj6ZI
                meta:
                  created: "2024-12-04T00:08:03.250Z"
                  lastModified: "2024-12-04T00:20:47.346Z"
                  resourceType: Group
                schemas:
                  - "urn:ietf:params:scim:schemas:core:2.0:Group"
                members:
                  - display: blob.ross@blobsrus.co
                    value: 9e8719d9-276a-4964-9395-a493189a247c
        '400':
          description: Invalid request body
        '429':
          $ref: '#/components/responses/TooManyRequests'
    patch:
      tags:
        - User groups
      summary: Update user group
      description: |
        Update the specified user group by applying a list of [SCIM 2.0 patch operations](https://datatracker.ietf.org/doc/html/rfc7644#section-3.5.2). Use `PATCH` to add or remove individual members, or to rename the group, without affecting other attributes.

        To replace the entire group resource (including the full membership list) in a single request, use the [Replace user group](/api/user-groups/replace-user-group) endpoint.
      security:
        - orgApiKey: []
      operationId: updateUserGroup
      parameters:
        - name: userGroupId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the group to be updated
          example: mEhXj6ZI
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - Operations
                - schemas
              properties:
                schemas:
                  type: array
                  description: The SCIM schema URIs for the request. Must include `urn:ietf:params:scim:api:messages:2.0:PatchOp`.
                  items:
                    type: string
                    enum:
                      - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                  example:
                    - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                Operations:
                  type: array
                  minItems: 1
                  description: |
                    The list of patch operations to apply to the group. Supported operations:

                    | Operation | Description |
                    |-----------|-------------|
                    | `add` with `path: "members"` | Add one or more users to the group |
                    | `remove` with `path: 'members[value eq "{userId}"]'` | Remove a single user from the group |
                    | `replace` with `path: "displayName"` | Rename the group |
                    | `replace` with `path: "members"` | Replace the entire membership list |
                    | `replace` without a `path` | Replace top-level attributes (for example, `displayName`) |
                  items:
                    type: object
                    required:
                      - op
                    properties:
                      op:
                        type: string
                        description: The operation to perform.
                        enum:
                          - add
                          - replace
                          - remove
                      path:
                        type: string
                        description: |
                          The attribute path the operation targets. For member removal, use the filter syntax `members[value eq "{userId}"]`. Omit to apply the operation to the resource as a whole.
                        example: members
                      value:
                        description: The value to apply for the operation. Required for `add` and `replace` operations.
            examples:
              addMembers:
                summary: Add members to the group
                value:
                  schemas:
                    - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                  Operations:
                    - op: add
                      path: members
                      value:
                        - display: blob.ross@blobsrus.co
                          value: 9e8719d9-276a-4964-9395-a493189a247c
              removeMember:
                summary: Remove a member from the group
                value:
                  schemas:
                    - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                  Operations:
                    - op: remove
                      path: 'members[value eq "9e8719d9-276a-4964-9395-a493189a247c"]'
              renameGroup:
                summary: Rename the group
                value:
                  schemas:
                    - "urn:ietf:params:scim:api:messages:2.0:PatchOp"
                  Operations:
                    - op: replace
                      path: displayName
                      value: Blob SEs
      responses:
        '200':
          description: Group updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimGroup'
              example:
                displayName: Blob SEs
                id: mEhXj6ZI
                meta:
                  created: "2024-12-04T00:08:03.250Z"
                  lastModified: "2024-12-04T00:20:47.346Z"
                  resourceType: Group
                schemas:
                  - "urn:ietf:params:scim:schemas:core:2.0:Group"
                members:
                  - display: blob.ross@blobsrus.co
                    value: 9e8719d9-276a-4964-9395-a493189a247c
        '400':
          description: Invalid patch operations
        '429':
          $ref: '#/components/responses/TooManyRequests'
    delete:
      tags:
        - User groups
      summary: Delete user group
      description: Delete the specified user group.
      security:
        - orgApiKey: []
      operationId: deleteUserGroup
      parameters:
        - name: userGroupId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the group to be deleted
          example: mEhXj6ZI
      responses:
        '204':
          description: Group deleted successfully
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/user-groups/{userGroupId}/model-roles:
    post:
      tags:
        - User group model roles
      summary: Assign or update user group model role
      description: |
        Assign or update a model role for a user group. If the user group already has a role for the specified model, this endpoint will update it to the new role. All members of the user group will inherit this role.

        Model roles control what actions user group members can perform on models and connections. To manage user groups, see the [User group APIs](/api/user-groups).
      security:
        - bearerAuth: []
      operationId: assignUserGroupModelRole
      parameters:
        - name: userGroupId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the user group to assign or update a model role for.
          example: mEhXj6ZI
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - roleName
              properties:
                connectionId:
                  type: string
                  format: uuid
                  description: |
                    The ID of the connection that the model belongs to:
                    
                    - **Required** if `modelId` is not provided
                    - **Optional** if `modelId` is provided, as it will be inferred from the model
                modelId:
                  type: string
                  format: uuid
                  description: |
                    The ID of the model to assign the role for:
                    
                    - **Optional** when assigning `CONNECTION_ADMIN` or [custom roles](/administration/users/custom-roles) with `CONNECTION_ADMIN` as the base role
                    - **Required** for other role types
                roleName:
                  type: string
                  description: |
                    The role to assign. Available roles include:

                    - `VIEWER` - Can view the model
                    - `QUERIER` - Can view and query the model
                    - `QUERY_TOPICS` - Can query specific topics. Equivalent to **Restricted Querier.**
                    - `MODELER` - Can edit and model the data
                    - `CONNECTION_ADMIN` - Full administrative access to the connection
                    - `NO_ACCESS` - No access to the model
                    - [Custom roles](/administration/users/custom-roles) defined for your organization
            example:
              connectionId: bc1f9c9f-208d-48a2-9ae3-ff80f2c79fed
              modelId: 7d3e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a
              roleName: QUERIER
      responses:
        '200':
          description: Model role assigned or updated successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  userGroupId:
                    type: string
                    description: The ID of the user group.
                  connectionId:
                    type: string
                    format: uuid
                    description: The ID of the connection.
                  modelId:
                    type: string
                    format: uuid
                    description: The ID of the model.
                  roleName:
                    type: string
                    description: The assigned role name.
              example:
                userGroupId: mEhXj6ZI
                connectionId: bc1f9c9f-208d-48a2-9ae3-ff80f2c79fed
                modelId: 7d3e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a
                roleName: QUERIER
        '400':
          description: |
            Bad Request. Possible error messages include:

            - `Invalid JSON`
            - `Invalid model ID`
            - `Invalid connection ID`
            - `Method not allowed`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible error messages include:

            - `User group not found in organization`
            - `Model does not exist`
            - `Connection does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: |
            Unprocessable Entity. Possible error messages include:

            - `Invalid role`
            - `Model does not belong to connection`
            - `Only shared and shared_extension models can be assigned model roles`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - User group model roles
      summary: Retrieve user group model roles
      description: |
        Retrieve the model role assignments for a user group.
      security:
        - bearerAuth: []
      operationId: getUserGroupModelRoles
      parameters:
        - name: userGroupId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the user group to retrieve model roles for.
          example: mEhXj6ZI
        - name: modelId
          in: query
          schema:
            type: string
            format: uuid
          description: Filter results to a specific model ID. If not provided, returns roles for all models the user group has access to.
          example: 7d3e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a
        - name: connectionId
          in: query
          schema:
            type: string
            format: uuid
          description: Filter results to models from a specific connection. If not provided, returns roles for all connections.
          example: bc1f9c9f-208d-48a2-9ae3-ff80f2c79fed
      responses:
        '200':
          description: User group model roles retrieved successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  userGroupId:
                    type: string
                    description: The ID of the user group.
                  results:
                    type: array
                    description: Array of role assignments for the user group.
                    items:
                      type: object
                      properties:
                        modelId:
                          type: string
                          format: uuid
                          description: The ID of the model.
                        connectionId:
                          type: string
                          format: uuid
                          description: The ID of the connection.
                        roleName:
                          type: string
                          description: The role assigned to the user group for this model.
                        baseRole:
                          type: string
                          description: The base role assigned to the user group.
              example:
                userGroupId: mEhXj6ZI
                results:
                  - baseRole: QUERIER
                    roleName: QUERIER
                    connectionId: bc1f9c9f-208d-48a2-9ae3-ff80f2c79fed
                    modelId: 7d3e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Invalid request - connectionId or modelId is not a UUID`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '404':
          description: User group not found in organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: User group not found in organization
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/users/{userId}/model-roles:
    post:
      tags:
        - User model roles
      summary: Assign or update user model role
      description: |
        Assign or update a model role for a user. If the user already has a role for the specified model, this endpoint will update it to the new role.

        Model roles control what actions a user can perform on models and connections. To manage users, see the [User APIs](/api/users).
      security:
        - bearerAuth: []
      operationId: assignUserModelRole
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the user to assign or update a model role for.
          example: 9e8719d9-276a-4964-9395-a493189a247c
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - roleName
              properties:
                connectionId:
                  type: string
                  format: uuid
                  description: |
                    The ID of the connection that the model belongs to:
                    
                    - **Required** if `modelId` is not provided
                    - **Optional** if `modelId` is provided, in which case it will be inferred from the model
                modelId:
                  type: string
                  format: uuid
                  description: |
                    The ID of the model to assign the role for:
                    
                    - **Optional** when assigning `CONNECTION_ADMIN` or [custom roles](/administration/users/custom-roles) with `CONNECTION_ADMIN` as the base role
                    - **Required** for other role types
                roleName:
                  type: string
                  description: |
                    The role to assign. Available roles include:

                    - `VIEWER` - Can view the model
                    - `QUERIER` - Can view and query the model
                    - `QUERY_TOPICS` - Can query specific topics. Equivalent to **Restricted Querier**.
                    - `MODELER` - Can edit and model the data
                    - `CONNECTION_ADMIN` - Full administrative access to the connection
                    - `NO_ACCESS` - No access to the model
                    - [Custom roles](/administration/users/custom-roles) defined for your organization
            example:
              connectionId: bc1f9c9f-208d-48a2-9ae3-ff80f2c79fed
              modelId: 7d3e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a
              roleName: QUERIER
      responses:
        '200':
          description: Model role assigned or updated successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  userId:
                    type: string
                    format: uuid
                    description: The ID of the user.
                  connectionId:
                    type: string
                    format: uuid
                    description: The ID of the connection.
                  modelId:
                    type: string
                    format: uuid
                    description: The ID of the model.
                  roleName:
                    type: string
                    description: The assigned role name.
              example:
                userId: 9e8719d9-276a-4964-9395-a493189a247c
                connectionId: bc1f9c9f-208d-48a2-9ae3-ff80f2c79fed
                modelId: 7d3e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a
                roleName: QUERIER
        '400':
          description: |
            Bad Request. Possible error messages include:

            - `Invalid JSON`
            - `Invalid model ID`
            - `Invalid connection ID`
            - `Method not allowed`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Not Found. Possible error messages include:

            - `User not found in organization`
            - `Model does not exist`
            - `Connection does not exist`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: |
            Unprocessable Entity. Possible error messages include:

            - `Invalid role`
            - `Model does not belong to connection`
            - `Only shared and shared_extension models can be assigned model roles`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      tags:
        - User model roles
      summary: Retrieve user model roles
      description: |
        Retrieve the model role assignments for a user. This includes both direct role assignments and roles inherited from user group memberships.
      security:
        - bearerAuth: []
      operationId: getUserModelRoles
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: The ID of the user to retrieve model roles for.
          example: 9e8719d9-276a-4964-9395-a493189a247c
        - name: modelId
          in: query
          schema:
            type: string
            format: uuid
          description: Filter results to a specific model ID. If not provided, returns roles for all models the user has access to.
          example: 7d3e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a
        - name: connectionId
          in: query
          schema:
            type: string
            format: uuid
          description: Filter results to models from a specific connection. If not provided, returns roles for all connections.
          example: bc1f9c9f-208d-48a2-9ae3-ff80f2c79fed
      responses:
        '200':
          description: User model roles retrieved successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  membershipId:
                    type: string
                    format: uuid
                    description: The ID of the user's membership in the organization.
                  results:
                    type: array
                    description: Array of all role assignments for the user, including direct assignments, roles inherited from user groups, and connection base roles.
                    items:
                      type: object
                      properties:
                        modelId:
                          type: string
                          format: uuid
                          description: The ID of the model.
                        connectionId:
                          type: string
                          format: uuid
                          description: The ID of the connection.
                        roleName:
                          type: string
                          description: The role name for this assignment.
                        baseRole:
                          type: string
                          description: The base role for this assignment.
                        priority:
                          type: integer
                          description: The priority of this role assignment. Higher values take precedence.
                        resolved:
                          type: boolean
                          description: If `true`, this is the highest priority role for the model. This is the role that will be used when determining the user's effective permissions.
                        from:
                          type: object
                          description: Information about where this role assignment comes from.
                          properties:
                            type:
                              type: string
                              description: |
                                The type of role assignment:

                                - `User Role` - Direct role assignment to the user
                                - `Group Role` - Role inherited from a user group
                                - `Connection Base Role` - Default role from the connection
                            miniUuid:
                              type: string
                              description: The short ID of the user group. Only present for `Group Role` type.
                            name:
                              type: string
                              description: The name of the user group. Only present for `Group Role` type.
                            depth:
                              type: integer
                              description: The depth of group nesting. Only present for `Group Role` type.
              example:
                membershipId: 9633bd79-7bdf-4773-8952-8fdd4098e51c
                results:
                  - baseRole: MODELER
                    from:
                      type: User Role
                    priority: 350
                    resolved: true
                    roleName: MODELER
                    connectionId: 8a464dc9-1f0e-4a9e-86fa-e1e6d970157c
                    modelId: 5fb90312-67b1-4cca-823a-9a341d549320
                  - baseRole: QUERIER
                    from:
                      depth: 0
                      miniUuid: PgjffoEu
                      name: Super Group
                      type: Group Role
                    priority: 250
                    resolved: false
                    roleName: QUERIER
                    connectionId: 8a464dc9-1f0e-4a9e-86fa-e1e6d970157c
                    modelId: 5fb90312-67b1-4cca-823a-9a341d549320
                  - baseRole: VIEWER
                    from:
                      type: Connection Base Role
                    priority: 50
                    resolved: false
                    roleName: VIEWER
                    connectionId: 8a464dc9-1f0e-4a9e-86fa-e1e6d970157c
                    modelId: 5fb90312-67b1-4cca-823a-9a341d549320
        '400':
          description: |
            Bad Request

            Possible error messages:

            - `Invalid request - id, connectionId or modelId is not a UUID`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '404':
          description: User not found in organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
              example:
                detail: User not found in organization
                status: 404
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v2/documents:
    post:
      tags:
        - Documents v2
      summary: Create and publish document
      description: |
        Create and publish a brand-new document.

        Because this is the first publication of brand-new content, it is not subject to the organization's `requirePullRequestToPublish` policy, which gates edits to existing content.
      x-mint:
        href: /api/documents-v2/create-document
        content: |
          ## Content sections

          - **`queryPresentations`** — Tabs/tiles keyed by record key in `queryPresentations.data` (e.g. `"1"`, `"2"`). `data` is shallow-merged by key; `order` (when present) replaces the tab order. Tile keys don't need to start at `"1"` — if the request supplies any tiles of its own, the blank seed tile normally created at key `"1"` is dropped automatically. A request with no tiles still gets that single blank seed tile at `"1"`.
          - **`controls`** — Dashboard filters/controls keyed by control ID. `data` is shallow-merged by key (`data.{id}: null` deletes a control); `order` (when present) replaces the control order.
          - **`settings`** — Dashboard settings, shallow-merged per key: `controlBar` (`collapsible` / `defaultExpanded` / `fixed`), `crossfilterEnabled`, `customText` (`queryError` / `queryNoResults`), `facetFilters`, `refreshInterval`, `runQueriesOn`.
          - **`containers`** — The dashboard layout. When present it fully replaces the existing layout (grid / stack / page / reference containers, recursively nested).

          ## Creating an app document

          <Note>
            The `app` property is currently in development and may change.
          </Note>

          Send `app: { html, settings? }` to create the document as an app instead of a dashboard. Apps must be enabled for the organization or the API will return an error. If the caller is using a PAT, the token must have permission to create apps.

          `app` is mutually exclusive with `containers` (including an explicit `null`), `controls`, and `settings` — combining `app` with any of these returns a `400`. `queryPresentations` can still be sent alongside `app`.

          ## Migrating from v1

          The v1 one-shot `PUT /v1/documents/{documentId}` maps to the v2 **Create > Patch draft > Publish** workflow. For a brand-new document, a single request to this endpoint is enough. To edit an existing document, use [Create draft and patch document](/api/documents-v2/create-draft-and-patch-document) then [Publish draft](/api/documents-v2/publish-draft).

          | v1 `PUT` field | v2 location |
          |----------------|-------------|
          | `modelId` | `modelId` (returned on read; accepted but immutable on patch) |
          | `name` / `description` | `name` / `description` |
          | `facetFilters` (top-level) | `settings.facetFilters` |
          | `refreshInterval` (top-level) | `settings.refreshInterval` |
          | `filterConfig` / `filterOrder` | `controls.data` / `controls.order` |
          | `documentMetadata` (presentation) | `settings.*` / `containers` |
          | `queryPresentations` (array) | `queryPresentations.data` (keyed) + `queryPresentations.order` |
          | `clearExistingDraft` | **Removed.** A pre-existing draft now returns `409`; patch that draft or publish/discard it instead. |

          There is no one-shot `PATCH` endpoint for full document updates that includes identifier changes. To rename a published document's identifier, use the [Rename document identifier endpoint](/api/documents-v2/rename-document-identifier).
          
          There is also no `narrative` field — AI summaries attach as `linked`-type query presentations. See [Create draft and patch document](/api/documents-v2/create-draft-and-patch-document#ai-summaries-via-linked-tiles) for more information.
      security:
        - bearerAuth: []
      operationId: documentsV2Create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentsV2CreateBody'
            examples:
              minimal:
                summary: Create an empty document
                value:
                  modelId: "b81e4679-1234-4abc-9def-0123456789ab"
                  name: "Q2 Revenue"
              withContent:
                summary: Create a document with one tile and a custom identifier
                value:
                  modelId: "b81e4679-1234-4abc-9def-0123456789ab"
                  identifier: "q2-revenue"
                  name: "Q2 Revenue"
                  description: "Quarterly revenue review"
                  queryPresentations:
                    data:
                      "1":
                        type: "query"
                        name: "Revenue by month"
                        topicName: "order_items"
                        prefersChart: true
                        query:
                          fields:
                            - "order_items.created_at[month]"
                            - "order_items.sale_price_sum"
                  settings:
                    crossfilterEnabled: true
                    refreshInterval: 3600
              withApp:
                summary: "Create an app document"
                value:
                  modelId: "b81e4679-1234-4abc-9def-0123456789ab"
                  name: "Sales app"
                  app:
                    html: "<!DOCTYPE html><html>...</html>"
              withForeignTab:
                summary: "Create a document with a cross-model (foreign) tab"
                value:
                  modelId: "b81e4679-1234-4abc-9def-0123456789ab"
                  name: "Q2 Revenue"
                  queryPresentations:
                    data:
                      "2":
                        type: "foreign"
                        name: "Marketing spend"
                        foreignModelId: "c3d4e5f6-1234-4abc-9def-0123456789ab"
                        topicName: "campaigns"
                        query:
                          fields:
                            - "campaigns.name"
                            - "campaigns.spend"
      responses:
        '201':
          description: |
            Document created and published successfully. 
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsV2CreateResponse'
              example:
                identifier: "q2-revenue"
                name: "Q2 Revenue"
                description: "Quarterly revenue review"
        '400':
          description: |
            Invalid request body or schema validation error. Possible causes:
            
            - An unknown top-level field
            - Length cap exceeded (`name must be 254 characters or fewer`)
            - Query-presentation cap exceeded (`A single patch may carry at most 48 query presentations.`)
            - The `identifier` is already in use
            - `app` combined with `containers`, `controls`, or `settings`. Also returned when `fileUploadId` is provided on a non-CSV/spreadsheet tab type, when the upload ID doesn't correspond to an upload in the caller's organization, or when `app.html` is empty or exceeds the 2 MiB cap.
            - A `foreign` tab naming the document's own model, missing `foreignModelId` or `query`, or `foreignModelId` supplied on a non-`foreign` tab.
            - A `linked` tile's `sourceQueryPresentationKey` points at a key not present in the final document. This includes `"1"` when the request supplies its own tiles — the blank seed normally created at that key is dropped, so a `linked` tile can no longer source it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Insufficient permissions to create a document on this model, or on the destination folder. Editor permissions or higher are required on the destination folder; does not apply to Organization Admins.
            
            For apps, also returned when apps aren't enabled for the organization, or when a PAT's role lacks the `allowCreateApps` permission on the model. Also returned when a `foreign` tab's `foreignModelId` names a model you can't build workbooks on — this includes an unknown, deleted, or inaccessible model, so the error doesn't reveal which models exist.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Base model, branch, or destination folder not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'

  /v2/documents/{documentId}:
    get:
      tags:
        - Documents v2
      summary: Get document state
      description: |
        Retrieve the document's published state. The response can be used as-is as the request body in a call to the [Patch draft API](/api/documents-v2/patch-draft).

        If `documentId` names a draft instead of a published document, the response also includes `draftOf.identifier`, the published document the draft belongs to. To read a draft's state, use [Get draft state](/api/documents-v2/get-draft-state), which returns the same `draftOf` field. To read an app document's HTML, use [Read app content](/api/apps/read-app-content).
      x-mint:
        content: |
          ## Query tiles

          Tiles in `queryPresentations.data` are keyed by a stable record key (e.g. `"1"`, `"2"`); the server uses that key to identify existing tiles on a later patch, so you never need to track any other identifier. Control IDs and container `instanceKey` / `referenceKey` values also round-trip unchanged.

          ## Workbook-only documents

          Workbook-only documents return only the workbook-scoped fields (`name`, `description`, `queryPresentations`); the dashboard-scoped `containers`, `controls`, and `settings` are omitted until a layout exists.

          ## App documents

          App documents return the workbook-scoped fields plus an `app` property whose `url` is the app sub-resource, where the app's HTML and settings are read and written — the document body itself doesn't include anything app-scoped. The `app` property is omitted (the document reads as workbook-only) when apps are disabled for the organization.
      security:
        - bearerAuth: []
      operationId: documentsV2Get
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The document's URL slug (e.g. `abc123`) or its canonical workbook UUID. Also accepts a draft identifier, in which case the response is the draft's state with `draftOf.identifier` naming the published document.
        - name: pretty
          in: query
          required: false
          schema:
            type: boolean
          description: Format response with 2-space indentation for readability. Accepts `true`, `1`, or no value (e.g., `?pretty`). Default is compact (single-line) JSON.
      responses:
        '200':
          description: Document state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsV2ReadResponse'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions to read the document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Document not found. Also returned when `documentId` names a draft rather than a published document — read a draft's state with [Get draft state](/api/documents-v2/get-draft-state) instead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: |
            The document uses the classic dashboard layout; upgrade to the advanced layout first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v2/documents/{documentId}/draft:
    patch:
      tags:
        - Documents v2
      summary: Create draft and patch document
      description: |
        Create a new draft on the published document and apply the patch. There is no auto-publish — the response includes the new `draftIdentifier` for follow-up calls. Pass an optional `branchId` to attach the draft to a branch; omit it for a draft on the main (unpublished) workspace.
      x-mint:
        content: |
          ## Draft and publish workflow

          Editing an existing document is a three-step flow:

          1. **`PATCH /api/v2/documents/{documentId}/draft`** *(this endpoint)* — creates a new draft on the published document and applies your patch. Returns the new `draftIdentifier`.
          2. *(optional)* [**`PATCH /api/v2/documents/{documentId}/draft/{draftId}`**](/api/documents-v2/patch-draft) — apply further patches to that draft.
          3. [**`POST /api/v2/documents/{documentId}/draft/publish`**](/api/documents-v2/publish-draft) — publish the main draft live.

          A document may have only one main draft at a time. If one already exists, this endpoint returns `409` — patch the existing draft (step 2), publish it, or discard it with `DELETE /api/v1/documents/{documentId}/draft`. This replaces v1's `clearExistingDraft` flag, which **does not exist** in v2.

          A `GET` response is a valid `PATCH` body — [read the current state](/api/documents-v2/get-document-state), edit it, and submit it back verbatim.

          ## Addressing tiles (no `miniUuid`)

          Tiles are addressed by their record key in `queryPresentations.data` (e.g. `"2"`). The server owns internal tile identity, so you never send or manage `miniUuid`:

          - **New key** → a new tile is created.
          - **Existing key, identical content** (a verbatim round-trip of a `GET`) → treated as a **no-op** and dropped from the patch.
          - **Existing key, changed content** → the tile is updated (the server regenerates its internal identifier).
          - **`linked`-type tabs** always re-resolve — they are excluded from no-op detection.
          - Query presentations are immutable server-side: replace a tile by key, don't mutate it in place.
          - `model_extension_id` - The query-model binding. This is read-only and managed by Omni.

          `data` is **shallow-merged** by key — omitted keys are untouched; `data.{key}: null` deletes that tile. `order`, when present, replaces the tab order. `controls` and `settings` are likewise shallow-merged; `containers`, when present, fully replaces the layout.

          ## AI summaries via linked tiles

          There is no `narrative` field. To attach an AI-style summary that reuses another tile's query, add a `linked`-type query presentation whose `sourceQueryPresentationKey` points at the source tile's record key (a string such as `"2"` — **not** a positional index into `order`):

          ```json
          {
            "queryPresentations": {
              "data": {
                "3": {
                  "type": "linked",
                  "name": "Summary",
                  "sourceQueryPresentationKey": "2"
                }
              }
            }
          }
          ```

          ## Binding an upload as a Source tab

          To bind an existing upload (created via [POST /v1/uploads](/api/uploads/upload-csv-file)) as a document Source tab:

          1. [**`GET /api/v2/documents/{documentId}`**](/api/documents-v2/get-document-state) to read the document's `workbookModelId`
          2. [**`POST /api/v1/uploads`**](/api/uploads/upload-csv-file) with `modelId` set to the draft's workbook model (the connection decides warehouse staging vs S3-only)
          3. **`PATCH /api/v2/documents/{documentId}/draft`** *(this endpoint)* with a `csv` or `spreadsheet` tab whose `fileUploadId` is the upload ID returned in step 2
          4. [**`POST /api/v2/documents/{documentId}/draft/publish`**](/api/documents-v2/publish-draft) to publish

          ```json
          {
            "queryPresentations": {
              "data": {
                "2": {
                  "type": "csv",
                  "name": "Customer data",
                  "fileUploadId": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
                }
              }
            }
          }
          ```

          Changing `fileUploadId` on an existing tab key repoints the tab at a different upload, regenerating the tab with the new upload's data. The same endpoints for [creating documents](/api/documents-v2/create-document) and [patching existing drafts](/api/documents-v2/patch-draft) accept `fileUploadId` on `csv` / `spreadsheet` tabs.

          ## Cross-model (foreign) tabs

          To add a [cross-model tab](/analyze-explore/cross-model-tabs) at a new tile key, set `type` to `foreign` and `foreignModelId` to the model its query should run against:

          ```json
          {
            "queryPresentations": {
              "data": {
                "3": {
                  "type": "foreign",
                  "name": "Marketing spend",
                  "foreignModelId": "c3d4e5f6-1234-4abc-9def-0123456789ab",
                  "topicName": "campaigns",
                  "query": {
                    "fields": ["campaigns.name", "campaigns.spend"]
                  }
                }
              }
            }
          }
          ```

          `foreignModelId` must name a shared model, other than the document's own base model, that you can build workbooks on. Naming the document's own model returns a `400`; naming a model you can't build workbooks on (including an unknown, deleted, or inaccessible model) returns a `403`.

          An existing tab can't be converted to or from `foreign`. Echoing or editing a stored `foreign` tab keeps its stored execution model — a supplied `foreignModelId` is ignored.
      security:
        - bearerAuth: []
      operationId: documentsV2PatchDraft
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The published document's URL slug (e.g. `abc123`) or its canonical workbook UUID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentsV2CreateDraftBody'
            examples:
              metadataOnly:
                summary: Rename the document and tweak a setting
                value:
                  name: "Q2 Revenue (final)"
                  summary: "Rename and enable cross-filtering"
                  settings:
                    crossfilterEnabled: true
              controlBar:
                summary: Make the control bar stay fixed while scrolling
                value:
                  settings:
                    controlBar:
                      fixed: true
              contentOnly:
                summary: Add a tile and delete another
                value:
                  queryPresentations:
                    data:
                      "3":
                        type: "query"
                        name: "Orders by region"
                        topicName: "order_items"
                        query:
                          fields:
                            - "users.state"
                            - "order_items.count"
                      "4": null
              linkedSummary:
                summary: Attach an AI summary linked to tile "2"
                value:
                  queryPresentations:
                    data:
                      "5":
                        type: "linked"
                        name: "Summary"
                        sourceQueryPresentationKey: "2"
              foreignTab:
                summary: Add a cross-model (foreign) tab
                value:
                  queryPresentations:
                    data:
                      "6":
                        type: "foreign"
                        name: "Marketing spend"
                        foreignModelId: "c3d4e5f6-1234-4abc-9def-0123456789ab"
                        topicName: "campaigns"
                        query:
                          fields:
                            - "campaigns.name"
                            - "campaigns.spend"
              onBranch:
                summary: Create the draft on a branch
                value:
                  branchId: "8b1f0a2c-1234-4abc-9def-0123456789ab"
                  name: "Q2 Revenue (branch edit)"
      responses:
        '200':
          description: Draft created and patch applied successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsV2PatchDraftResponse'
              example:
                identifier: "q2-revenue"
                draftIdentifier: "q2-revenue-draft-7f3a"
                name: "Q2 Revenue (final)"
                description: "Quarterly revenue review"
        '400':
          description: |
            Invalid request body or schema validation error. Potential causes:
            
            - An unknown top-level field
            - A length cap exceeded
            - query-presentation cap exceeded (`A single patch may carry at most 48 query presentations.`). 
            - `modelId` doesn't match the document's current base model
            - `fileUploadId` is provided on a non-csv/spreadsheet tab type
            - The upload ID doesn't correspond to an upload in the caller's organization
            - `foreign` tab names the document's own model or is missing `foreignModelId`/`query`
            - `foreignModelId` is supplied on a non-`foreign` tab
            - An existing tab is converted to or from `foreign`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions to update the document, or the target is an app document and the organization has apps disabled. Also returned when a new `foreign` tab's `foreignModelId` names a model you can't build workbooks on — this includes an unknown, deleted, or inaccessible model, so the error doesn't reveal which models exist.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Document or branch not found. Also returned when `documentId` names a draft rather than a published document — drafts only attach to published documents.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          description: |
            Possible causes:

            - A draft already exists for this document on the target branch. Patch it with [`PATCH /api/v2/documents/{documentId}/draft/{draftId}`](/api/documents-v2/patch-draft), publish it, or discard it with `DELETE /api/v1/documents/{documentId}/draft`.
            - A concurrent request just created the layout for this document
            - Document doesn't have an app but an app was supplied (`app` is currently read-only)
            - An app was supplied but its URL doesn't address the provided document's app
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: |
            The document cannot satisfy the patch: 
            
            - A classic layout dashboard. [Upgrade to the new layout](/api/documents/upgrade-dashboard-layout).
            - A workbook-only document patched with dashboard-scoped fields but no `containers` payload, or an empty one
            - An app document patched with dashboard-scoped fields. A document can have a dashboard or an app, but not both.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v2/documents/{documentId}/draft/publish:
    post:
      tags:
        - Documents v2
      summary: Publish draft
      description: |
        Publish the document's current main (non-branch) draft, promoting it to the published version. No request body — the draft is consumed, so the response echoes the now-published document's metadata.

        Only the main draft is publishable with this endpoint. A branch-attached draft is published by [merging its branch](/api/model-branches/merge-a-branch).
      security:
        - bearerAuth: []
      operationId: documentsV2PublishDraft
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The published document's URL slug (e.g. `abc123`) or its canonical workbook UUID.
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: Draft published successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsV2PublishDraftResponse'
              example:
                identifier: "q2-revenue"
                name: "Q2 Revenue (final)"
                description: "Quarterly revenue review"
        '400':
          description: |
            The document requires a pull request to publish ("Can't publish because this document can only be edited through a branch").
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Insufficient permissions to publish the draft. Also returned when a user-scoped key passes `userId` naming a different member, or when `userId` names an embed user with no live embed session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: |
            Document not found, it has no main draft to publish (a branch-attached draft is published by merging its branch), `documentId` names a draft rather than a published document, `userId` names a membership outside the organization, or `userId` names a member who hasn't accepted their invitation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'

  /v2/documents/{documentId}/draft/{draftId}:
    get:
      tags:
        - Documents v2
      summary: Get draft state
      description: |
        Read the named draft's state. The response can be used as a request body when [creating](/api/documents-v2/create-draft-and-patch-document) and [patching](/api/documents-v2/patch-draft) drafts. The response includes `draftOf.identifier`, the published document this draft belongs to.
      security:
        - bearerAuth: []
      operationId: documentsV2GetDraft
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The published document's URL slug (e.g. `abc123`) or its canonical workbook UUID.
        - name: draftId
          in: path
          required: true
          schema:
            type: string
          description: The draft workbook identifier, returned by [Create draft and patch document](/api/documents-v2/create-draft-and-patch-document).
        - name: pretty
          in: query
          required: false
          schema:
            type: boolean
          description: Format response with 2-space indentation for readability. Accepts `true`, `1`, or no value (e.g., `?pretty`). Default is compact (single-line) JSON.
      responses:
        '200':
          description: Draft state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsV2ReadResponse'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions to read the draft.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Document or draft not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: |
            The draft uses the classic dashboard layout; [upgrade to the new layout](/api/documents/upgrade-dashboard-layout).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      tags:
        - Documents v2
      summary: Patch draft
      description: |
        Apply a patch to an existing draft. The request body, content sections, field caps, and tile-addressing rules are identical to [Create draft and patch document](/api/documents-v2/create-draft-and-patch-document); this route does not accept `branchId`.

        This endpoint doesn't create or publish drafts.
      security:
        - bearerAuth: []
      operationId: documentsV2PatchDraftByIdentifier
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The published document's URL slug (e.g. `abc123`) or its canonical workbook UUID.
        - name: draftId
          in: path
          required: true
          schema:
            type: string
          description: The draft workbook identifier the patch is applied to.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              description: The content/metadata diff applied to a draft. All fields optional; only the sections present are changed.
              properties:
                modelId:
                  type: string
                  format: uuid
                  description: Immutable model identifier. Must match the document's current base model or the API will return a 400 error.
                workbookModelId:
                  type: string
                  format: uuid
                  description: Immutable model identifier. Must match the document's current workbook model or the API will return a 400 error.
                name:
                  type: string
                  minLength: 1
                  maxLength: 254
                  description: Document name.
                description:
                  type:
                    - string
                    - "null"
                  description: Document description.
                summary:
                  type: string
                  minLength: 1
                  maxLength: 255
                  description: Caller-supplied description of what this patch changes, written to the history audit trail. When omitted, Omni auto-generates one from the updated sections.
                queryPresentations:
                  $ref: '#/components/schemas/DocumentsV2QueryPresentationsPatch'
                controls:
                  $ref: '#/components/schemas/DocumentsV2ControlsPatch'
                settings:
                  $ref: '#/components/schemas/DocumentsV2SettingsPatch'
                containers:
                  allOf:
                    - $ref: '#/components/schemas/DocumentsV2Containers'
                    - description: Container layout. When present, fully replaces the existing layout.
                app:
                  $ref: '#/components/schemas/DocumentsAppReadOnly'
                draftOf:
                  type: object
                  additionalProperties: false
                  description: |
                    **Currently read-only**. The identifier of the published document that the draft belongs to. 
                  properties:
                    identifier:
                      type: string
            examples:
              contentOnly:
                summary: Update a tile's subtitle
                value:
                  queryPresentations:
                    data:
                      "2":
                        type: "query"
                        name: "Revenue by month"
                        subTitle: "Trailing 12 months"
                        topicName: "order_items"
                        query:
                          fields:
                            - "order_items.created_at[month]"
                            - "order_items.sale_price_sum"
      responses:
        '200':
          description: Patch applied to draft successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsV2PatchDraftResponse'
        '400':
          description: |
            Invalid request body or schema validation error. Potential causes:
            
            - An unknown top-level field
            - A length cap exceeded
            - query-presentation cap exceeded (`A single patch may carry at most 48 query presentations.`). 
            - `modelId` doesn't match the document's current base model
            - `fileUploadId` is provided on a non-csv/spreadsheet tab type
            - The upload ID doesn't correspond to an upload in the caller's organization
            - `foreign` tab names the document's own model or is missing `foreignModelId`/`query`
            - `foreignModelId` is supplied on a non-`foreign` tab
            - An existing tab is converted to or from `foreign`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions to update the draft, or the target is an app draft and the organization has apps disabled. Also returned when a new `foreign` tab's `foreignModelId` names a model you can't build workbooks on — this includes an unknown, deleted, or inaccessible model, so the error doesn't reveal which models exist.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Document or draft not found. Also returned when `documentId` names a draft rather than a published document — drafts only attach to published documents.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          description: |
            - A concurrent request just created the layout for this document
            - Document doesn't have an app but an app was supplied (`app` is currently read-only)
            - An app was supplied but its URL doesn't address the provided document's app
            - A `draftOf` was supplied naming a document other than the route's target (`draftOf` is currently read-only)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: |
            The document cannot satisfy the patch: 
            
            - A classic layout dashboard. [Upgrade to the new layout](/api/documents/upgrade-dashboard-layout).
            - A workbook-only document patched with dashboard-scoped fields but no `containers` payload, or an empty one
            - An app document patched with dashboard-scoped fields. A document can have a dashboard or an app, but not both.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  
  /v2/documents/{documentId}/draft/{draftId}/dashboard:
    delete:
      description: |-
        Remove the dashboard from an existing draft, leaving a workbook-only document. Documents that are already workbook-only will remain unchanged.

        Use the [Publish draft endpoint](/api/documents-v2/publish-draft) to apply the changes from this endpoint to the published version of the document. **Note**: Schedules will be removed when the document is published.
      operationId: documentsV2RemoveDashboard
      summary: Remove dashboard from document
      tags:
        - Documents
      parameters:
        - schema:
            type: string
            example: def456
          required: true
          description: ID of the existing draft, created from a successful call to the [Create draft and patch document endpoint](/api/documents-v2/create-draft-and-patch-document).
          name: draftId
          in: path
        - schema:
            type: string
            example: abc123
          required: true
          description: ID of the published document associated with the draft.
          name: documentId
          in: path
      responses:
        '200':
          description: Dashboard removed on the draft. The response includes the `draftIdentifier`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsV2PatchDraftResponse'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions to update the document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document or draft not found. Also returned when `documentId` names a draft rather than a published document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: The document is an app, not a dashboard.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v2/documents/{documentId}/identifier:
    put:
      tags:
        - Documents v2
      summary: Rename document identifier
      description: |-
        Rename a published document's identifier. The change is applied live and immediately. Changes do not go through the draft/publish workflow. The former identifier is recorded in the document's rename history to enable automatic redirects. Only published documents can be renamed.
        
        The new identifier must be a valid slug and unused by any other document in the organization. If the new value is identical to the existing value (case-sensitive), the API will return a `200` but not write a history record.
      security:
        - bearerAuth: []
      operationId: documentsV2UpdateIdentifier
      parameters:
        - schema:
            type: string
          required: true
          description: Document identifier — either the URL slug (e.g. `abc123`) or the canonical workbook UUID.
          name: identifier
          in: path
          example: q2-revenue
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - identifier
              additionalProperties: false
              properties:
                identifier:
                  type: string
                  minLength: 2
                  maxLength: 48
                  pattern: "^[a-z0-9_-]+$"
                  description: |-
                    Document identifier slug. Must be 2–48 characters, containing only lowercase letters (a-z), digits (0-9), hyphens (-), and underscores (_). Cannot be a reserved value. If omitted when creating a document, an identifier is auto-generated.
                  example: q2-revenue
            examples:
              renameDocument:
                summary: Rename a document identifier
                value:
                  identifier: "new-q2-revenue"
              caseChange:
                summary: Case-only change (case-sensitive rename)
                value:
                  identifier: "Q2-Revenue"
      responses:
        '200':
          description: Identifier renamed successfully.
          content:
            application/json:
              schema:
                type: object
                required:
                  - description
                  - identifier
                  - name
                properties:
                  identifier:
                    type: string
                    description: The document identifier after the rename.
                    example: new-slug
                  name:
                    type: string
                    description: Document name.
                    example: Q2 Revenue Analysis
                  description:
                    type:
                      - string
                      - "null"
                    description: Document description.
              example:
                identifier: "new-q2-revenue"
                name: "Q2 Revenue Analysis"
                description: "Quarterly revenue review"        
        '400':
          description: |-
            Invalid identifier format — must be 2–48 characters, contain only lowercase letters, digits, hyphens, and underscores, and not be a reserved value.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "identifier must be 2-48 characters"
                status: 400
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Insufficient permissions to rename the document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "You do not have permission to update this document"
                status: 403
        '404':
          description: Document not found or archived.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: "Document not found"
                status: 404
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          description: |-
            The target is a draft rather than a published document, or the requested identifier is already in use by another document in the organization (case-insensitive collision).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                collision:
                  summary: Identifier already in use
                  value:
                    detail: "identifier is already in use"
                    status: 409
                draftTarget:
                  summary: Target is not published
                  value:
                    detail: "Cannot rename identifier of non-published document"
                    status: 409
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v2/documents/{documentId}/app:
    get:
      tags:
        - Apps
      summary: Read app content
      description: |
        <Note>
          This API is currently in development and may change.
        </Note>

        Read the published app content, including its HTML and settings. The calling user or the user specified with the `userId` parameter must have view access to the app or the API will return an error.

        The response can be used to create a draft. Read the draft state before building, as an existing draft may have diverged from the published version in the response.
      security:
        - bearerAuth: []
      operationId: documentsGetApp
      x-experimental: true
      parameters:
        - name: documentId
          in: path
          required: true
          schema:
            type: string
          description: The document's URL slug (e.g. `abc123`) or its canonical workbook UUID.
        - name: pretty
          in: query
          required: false
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - 'true'
              - 'false'
          description: Set `true` or `1` to pretty-print (2-space indent) the response; `false` / `0` is compact. Key ordering is deterministic regardless.
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: The published app content.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AppHTMLSettings'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Insufficient permissions to read the document. Also returned when a Personal Access Token passes `userId` naming a different member, or when `userId` names an embed user with no live embed session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document not found, the document has no app, the organization has apps disabled, or `userId` does not name a member of the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v2/documents/{documentId}/draft/app:
    get:
      description: |-
        <Note>
          This API is currently in development and may change.
        </Note>

        Read the app content on the document's main (non-branch) draft. If a document doesn't have a main draft, the API will return a `404`.
      operationId: documentsGetMainDraftApp
      summary: Read app content on main draft
      tags:
        - Apps
      x-experimental: true
      parameters:
        - name: documentId
          schema:
            type: string
            example: abc123
          required: true
          description: Document identifier — either the URL slug (e.g. `abc123`) or the canonical workbook UUID.
          in: path
        - name: pretty
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - 'true'
              - 'false'
          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.
          in: query
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: The main draft's app content.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AppHTMLSettings'
              example:
                html: "<!doctype html><html><body><h1>Sales overview</h1></body></html>"
                settings:
                  allowClipboard: false
                  allowDefaultMapProviders: false
                  allowDownloads: false
                  allowExternalNavigation: false
                  allowInternalNavigation: false
                  externalNavOpensInNewTab: true
                  navAllowedDomains: []
                  navAllowedDomainsEnabled: false
                  safeDomains: []
                  safeDomainsEnabled: false
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Insufficient permissions to read the document. Also returned when a user-scoped key passes `userId` naming a different member, or when `userId` names an embed user with no live embed session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document not found, document has no main draft, the draft has no app, the organization has apps disabled, or `userId` does not name a member of the organization.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      description: |-
        <Note>
          This API is currently in development and may change.
        </Note>

        Create or reuse the document's main draft and replace the existing app. Use the `draftIdentifier` in the response to call the [Publish draft API](/api/documents-v2/publish-draft) and publish the changes.
      operationId: documentsPutAppAutoDraft
      summary: Create/reuse draft and replace app content
      tags:
        - Apps
      x-experimental: true
      parameters:
        - name: documentId
          schema:
            type: string
            description: Document identifier — either the URL slug (e.g. `abc123`) or the canonical workbook UUID.
            example: abc123
          required: true
          description: Document identifier — either the URL slug (e.g. `abc123`) or the canonical workbook UUID.
          in: path
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AppPutBody'
            examples:
              htmlOnly:
                summary: Replace the HTML, keep current settings
                value:
                  html: "<!doctype html><html><body><h1>Sales overview</h1></body></html>"
              htmlAndSettings:
                summary: Replace the HTML and allow an external script host
                value:
                  html: "<!doctype html><html><head><script src=\"https://cdn.example.com/chart.js\"></script></head><body><div id=\"chart\"></div></body></html>"
                  settings:
                    safeDomainsEnabled: true
                    safeDomains:
                      - "cdn.example.com"
      responses:
        '200':
          description: Draft created or reused and app written. Use the `draftIdentifier` in the response to call the [Publish draft API](/api/documents-v2/publish-draft) and publish the changes.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AppPutResponse'
              example:
                identifier: "q2-revenue"
                draftIdentifier: "q2-revenue-draft-7f3a"
                name: "Q2 Revenue (final)"
                description: "Quarterly revenue review"
                app:
                  settings:
                    allowClipboard: false
                    allowDefaultMapProviders: false
                    allowDownloads: false
                    allowExternalNavigation: false
                    allowInternalNavigation: false
                    externalNavOpensInNewTab: true
                    navAllowedDomains: []
                    navAllowedDomainsEnabled: false
                    safeDomains:
                      - "cdn.example.com"
                    safeDomainsEnabled: true
        '400':
          description: Invalid request body (unknown field, empty, or `html` exceeds the cap of 2 MiB).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Insufficient permissions: no write access to the document, apps not enabled for the organization, or (a user-scoped key, or an organization key acting as `userId`) the role does not allow creating apps. Also returned when a user-scoped key passes `userId` naming a different member, or when `userId` names an embed user with no live embed session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document not found, `documentId` names a draft rather than a published document, `userId` names a membership outside the organization, or `userId` names a member who hasn't accepted their invitation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: The document is a dashboard, not an app.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    patch:
      description: |-
        <Note>
          This API is currently in development and may change.
        </Note>

        Create or reuse an existing document's main draft and apply targeted edits to the app's HTML without resending the whole document.
      operationId: documentsPatchAppAutoDraft
      summary: Create/reuse draft and edit app content
      tags:
        - Apps
      x-experimental: true
      parameters:
        - name: documentId
          schema:
            type: string
            description: Document identifier — either the URL slug (e.g. `abc123`) or the canonical workbook UUID.
            example: abc123
          required: true
          description: Document identifier — either the URL slug (e.g. `abc123`) or the canonical workbook UUID.
          in: path
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AppPatchBody'
      responses:
        '200':
          description: Draft created or reused and edits applied. Use the `draftIdentifier` in the response to call the [Publish draft API](/api/documents-v2/publish-draft) and publish the changes.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AppPutResponse'
        '400':
          description: |
            Invalid request body (unknown field, neither `htmlEdits` nor `settings`, or more than 20 `htmlEdits`), or a failed `htmlEdits` entry (not found, ambiguous, or result over the 2 MiB cap). Nothing is applied.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Insufficient permissions: no write access to the document, apps not enabled for the organization, or (a user-scoped key, or an organization key acting as `userId`) the role does not allow creating apps. Also returned when a user-scoped key passes `userId` naming a different member, or when `userId` names an embed user with no live embed session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document not found, `userId` names a membership outside the organization, or `userId` names a member who hasn't accepted their invitation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: There is no app to patch.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: The document is a dashboard, not an app.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v2/documents/{documentId}/draft/{draftId}/app:
    get:
      description: |-
        <Note>
          This API is currently in development and may change.
        </Note>

        Read the app content on a specified draft.
      operationId: documentsGetDraftApp
      summary: Read app content on a draft
      tags:
        - Apps
      x-experimental: true
      parameters:
        - name: draftIdentifier
          schema:
            type: string
            example: def456
          required: true
          description: Draft identifier. Use [List document drafts](/api/documents/list-document-drafts) to retrieve draft IDs.
          in: path
        - name: documentId
          schema:
            type: string
            example: abc123
          required: true
          description: Published document identifier.
          in: path
        - name: pretty
          schema:
            type: string
            enum:
              - '0'
              - '1'
              - 'true'
              - 'false'
          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.
          in: query
      responses:
        '200':
          description: The draft's app content.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AppHTMLSettings'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Insufficient permissions to read the document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document or draft not found, the draft has no app, or the organization has apps disabled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      description: |-
        <Note>
          This API is currently in development and may change.
        </Note>

        Replace the app HTML on an existing draft. If the specified document does not yet have an app, one will be created. 
      operationId: documentsPutApp
      summary: Replace app content on a draft
      tags:
        - Apps
      x-experimental: true
      parameters:
        - name: draftId
          schema:
            type: string
            example: def456
          required: true
          description: Draft workbook identifier. Use [List document drafts](/api/documents/list-document-drafts) to retrieve draft IDs.
          in: path
        - name: documentId
          schema:
            type: string
            example: abc123
          required: true
          description: Published document identifier.
          in: path
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AppPutBody'
      responses:
        '200':
          description: App written on the draft.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AppPutResponse'
        '400':
          description: Invalid request body (unknown field, empty, or `html` exceeds the cap of 2 MiB).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Insufficient permissions: no write access to the document, apps not enabled for the organization, or (user-scoped keys) the role does not allow creating apps.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document or draft not found. Also returned when `documentId` names a draft rather than a published document — drafts only attach to published documents.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: The draft is a dashboard, not an app; remove the dashboard with the [Remove dashboard from document API](/api/documents/remove-dashboard-from-document).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      summary: Edit app content on a draft
      operationId: documentsPatchApp
      description: |-
        <Note>
          This API is currently in development and may change.
        </Note>

        Apply targeted changes to an existing app on an existing draft without resending the HTML. Changes are all or nothing — an edit that matches zero times, matches more than once without `replaceAll`, or would push the HTML past the 2 MiB cap rejects the whole request with 400 naming the edit, and nothing is applied. This endpoint does not create an app.

        Use the `draftIdentifier` in the response to call the [Publish draft API](/api/documents-v2/publish-draft) and publish the changes.
      tags:
        - Apps
      x-experimental: true
      parameters:
        - name: draftId
          schema:
            type: string
            example: def456
          required: true
          description: Draft workbook identifier. Use [List document drafts](/api/documents/list-document-drafts) to retrieve draft IDs.
          in: path
        - name: documentId
          schema:
            type: string
            example: abc123
          required: true
          description: Published document identifier.
          in: path
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AppPatchBody'
      responses:
        '200':
          description: Edits applied on the draft. `warnings` names any resource hosts the app’s iframe CSP will block until an Organization Admin allows them.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AppWriteResponse'
        '400':
          description: |
            Invalid request body (unknown field, neither `htmlEdits` nor `settings`, or more than 20 `htmlEdits`), or a failed `htmlEdits` entry (not found, ambiguous, or result over the 2 MiB cap). Nothing is applied.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Insufficient permissions: no write access to the document, apps not enabled for the organization, or (user-scoped keys) the role does not allow creating apps.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document or draft not found. Also returned when `documentId` names a draft rather than a published document — drafts only attach to published documents.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: The draft has no app to patch.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError409'
        '422':
          description: The draft is a dashboard, not an app.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      summary: Remove app from a draft
      description: |-
        <Note>
          This API is currently in development and may change.
        </Note>

        Remove the app from an existing draft. Use the `draftIdentifier` in the response to call the [Publish draft API](/api/documents-v2/publish-draft) and publish the changes.
      operationId: documentsRemoveApp
      tags:
        - Apps
      x-experimental: true
      parameters:
        - name: draftId
          schema:
            type: string
            example: def456
          required: true
          description: Draft workbook identifier. Use [List document drafts](/api/documents/list-document-drafts) to retrieve draft IDs.
          in: path
        - name: documentId
          schema:
            type: string
            example: abc123
          required: true
          description: Published document identifier.
          in: path
      responses:
        '200':
          description: App removed on the draft.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentsV2PatchDraftResponse'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: |
            Insufficient permissions: no write access to the document, apps not enabled for the organization, or (user-scoped keys) the role does not allow creating apps.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: Document or draft not found. Also returned when `documentId` names a draft rather than a published document — drafts only attach to published documents.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          description: Method not allowed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: The draft is a dashboard, not an app; there is no app to remove.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/apps/{documentId}/download:
    post:
      tags:
        - App downloads
      summary: Initiate app download
      operationId: appsDownload
      description: |
        Start an asynchronous download job for a published app, rendered headlessly the same way a scheduled app delivery renders it. Every query in the app's workbook runs, including linked tabs; queries on a model the downloading user cannot query are omitted. Only the app's default view is rendered.

        After starting a job, poll [Get app download status](/api/app-downloads/get-app-download-status) for completion, then retrieve the file from [Get app download file](/api/app-downloads/get-app-download-file).

        Only one download per app, per user is allowed at a time. If a download is already in progress, the request returns a `409 Conflict` response with the existing job ID.
      parameters:
        - $ref: '#/components/parameters/documentId'
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: true
        content:
          application/json:
            schema:  # AppsDownloadBody
              type: object
              properties:
                filename:
                  type: string
                  minLength: 1
                  maxLength: 255
                  description: Custom filename for the downloaded file, without extension. Defaults to the app name.
                  example: territory-planner
                format:
                  type: string
                  enum:
                    - png
                    - pdf
                    - xlsx
                    - csv
                  description: |
                    Specifies the output format:
                    
                    - `png` - A screenshot of the rendered app
                    - `pdf` - The rendered app as one page
                    - `xlsx` - A screenshot cover sheet plus one sheet per query
                    - `csv` - A ZIP of one CSV per query
                  example: png
              required:
                - format
              additionalProperties: false
            examples:
              png:
                summary: Download as PNG
                value:
                  format: png
              pdfWithFilename:
                summary: Download as PDF with a custom filename
                value:
                  format: pdf
                  filename: territory-planner
      responses:
        '200':
          description: Download job initiated successfully.
          content:
            application/json:
              schema:  # AppsDownloadResponse
                type: object
                properties:
                  job_id:
                    type: string
                    format: uuid
                    description: ID of the download job. Use this to poll for status and fetch the file.
                    example: 123e4567-e89b-12d3-a456-426614174000
                  message:
                    type: string
                    description: Status message
                    example: Download initiated successfully
                required:
                  - job_id
                  - message
              example:
                job_id: 123e4567-e89b-12d3-a456-426614174000
                message: Download initiated successfully
        '400':
          description: Invalid request body, such as a missing or invalid `format`, or an unrecognized field.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Apps or scheduled app deliveries aren't enabled for the organization, the user can't view or download the app, or a Personal Access Token named another user.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: App not found, not published, archived, or the document has no app.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          description: A download of this app is already in progress for this user.
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    description: Error message
                  existing_job_id:
                    type: string
                    format: uuid
                    description: ID of the in-progress job
                  status:
                    type: string
                    description: Status of the existing job
              example:
                detail: A download is already in progress for this app. Please wait for it to complete or check its status.
                existing_job_id: 123e4567-e89b-12d3-a456-426614174000
                status: RENDERING
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          description: The download job could not be started.

  /v1/apps/{documentId}/download/{jobId}/status:
    get:
      tags:
        - App downloads
      summary: Get app download status
      operationId: appsDownloadStatus
      description: |
        Poll the status of an app download job started by [Initiate app download](/api/app-downloads/initiate-app-download). Poll until `status` is `complete`, then fetch the file from [Get app download file](/api/app-downloads/get-app-download-file).
      x-mint:
        content: |
          ## Job visibility

          Download jobs are visible only to the user the job runs as. If a job was started with a `userId` parameter, fetch the results with the same `userId`.

          Additionally, the specified user must still have access to the app when fetching the results using this endpoint.
      parameters:
        - $ref: '#/components/parameters/documentId'
        - $ref: '#/components/parameters/userId'
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: 123e4567-e89b-12d3-a456-426614174000
          description: The job ID returned by [Initiate app download](/api/app-downloads/initiate-app-download).
      responses:
        '200':
          description: Download job status.
          content:
            application/json:
              schema:  # AppsDownloadStatusResponse
                type: object
                properties:
                  created_at:
                    type: string
                    description: When the job was created (ISO 8601)
                    example: '2026-10-13T18:30:00.000Z'
                  error:
                    type: string
                    description: |
                      Why the job failed. Present only when `status` is `error`.
                  format:
                    type: string
                    enum:
                      - link_only
                      - pdf
                      - png
                      - csv
                      - xlsx
                      - json
                      - ai_prompt
                      - ai_summary_text
                    description: |
                      Output format. Omitted when `status` is `error`.
                    example: png
                  job_id:
                    type: string
                    description: |
                      Download job ID. Omitted when `status` is `error`.
                  status:
                    type: string
                    enum:
                      - in_progress
                      - complete
                      - error
                    description: |
                      Current status of the download job:
                      
                      - `in_progress` while the app renders
                      - `complete` once the file can be fetched
                      - `error` if the job failed
                    example: complete
                required:
                  - created_at
                  - status
              examples:
                inProgress:
                  summary: Job in progress
                  value:
                    created_at: '2026-10-13T18:30:00.000Z'
                    status: in_progress
                complete:
                  summary: Job complete
                  value:
                    created_at: '2026-10-13T18:30:00.000Z'
                    format: png
                    job_id: 123e4567-e89b-12d3-a456-426614174000
                    status: complete
                error:
                  summary: Job failed
                  value:
                    created_at: '2026-10-13T18:30:00.000Z'
                    status: error
                    error: Job failed to render.
        '400':
          description: '`jobId` is not a UUID.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: A Personal Access Token named another user.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: App or download job not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '429':
          $ref: '#/components/responses/TooManyRequests'

  /v1/apps/{documentId}/download/{jobId}:
    get:
      tags:
        - App downloads
      summary: Get app download file
      operationId: appsDownloadFile
      description: |
        Stream the finished file for an app download job. Poll [Get app download status](/api/app-downloads/get-app-download-status) until `status` is `complete` first. Finished files are retained for about a day.
      x-mint:
        content: |
          ## Job visibility

          Download jobs are visible only to the user the job runs as. If a job was started with a `userId` parameter, fetch the results with the same `userId`.

          Additionally, the specified user must still have access to the app when fetching the results using this endpoint.
      parameters:
        - $ref: '#/components/parameters/documentId'
        - $ref: '#/components/parameters/userId'
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: 123e4567-e89b-12d3-a456-426614174000
          description: The job ID returned by [Initiate app download](/api/app-downloads/initiate-app-download).
      responses:
        '200':
          description: |
            The file, streamed, with `Content-Type`, `Content-Length`, and `Content-Disposition: attachment` set.

            The `Content-Type` indicates the format of the download:

            | Format | Content-Type | Extension |
            |--------|--------------|-----------|
            | `png` | `image/png` | `.png` |
            | `pdf` | `application/pdf` | `.pdf` |
            | `xlsx` | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` | `.xlsx` |
            | `csv` | `application/zip` (one CSV per query) | `.zip` |
          content:
            image/png:
              schema:
                type: string
                format: binary
            application/pdf:
              schema:
                type: string
                format: binary
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
            application/zip:
              schema:
                type: string
                format: binary
                description: One CSV file per query, for the `csv` format.
        '202':
          description: The download job is still running. Poll the status endpoint first.
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    description: Error message
                  status:
                    type: string
                    description: Current job status
              example:
                detail: Download not ready yet. Please poll the status endpoint.
                status: in_progress
        '400':
          description: |
            `jobId` is not a UUID.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError400'
        '401':
          description: Missing or invalid authentication.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError401'
        '403':
          description: Apps or scheduled app deliveries aren't enabled for the organization, the user can no longer view or download the app, or a Personal Access Token named another user.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError403'
        '404':
          description: App or download job not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError404'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '410':
          description: The download job failed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    description: Error message
                  status:
                    type: string
                    description: Job status
              example:
                detail: Download job failed
                status: error
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          description: The file could not be read from storage.

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Can be either an [Organization API Key](/api/authentication#organization-api-keys) or [Personal Access Token (PAT)](/api/authentication#token-types).

        Include in the `Authorization` header as: `Bearer YOUR_TOKEN`
    orgApiKey:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Requires an [Organization API Key](/api/authentication#organization-api-keys). Personal Access Tokens (PATs) are not supported for this endpoint.

        Include in the `Authorization` header as: `Bearer ORGANIZATION_API_KEY`

  parameters:
    branchId:
      name: branch_id
      schema:
        type: string
        format: uuid
        example: 123e4567-e89b-12d3-a456-426614174001
      required: false
      description: |
        Branch ID to use for branch-aware operations. Accepts either the org-wide branch ID (an `orgBranchId`, also returned by the [Branches API](/api/branches/list-branches)) or the ID of that branch's layer on this specific model.
      in: query
    
    branchIdPath:
      name: branch_id
      in: path
      schema:
        type: string
        format: uuid
        example: 123e4567-e89b-12d3-a456-426614174001
      required: true
      description: |
        The organization-wide branch ID (`orgBranchId`). This isn't the same as the ID of a branch model.
    
    cursor:
      name: cursor
      schema:
        type: string
        example: eyJpZCI6IjEyMzQ1In0
      required: false
      description: |
        Cursor for pagination from previous response `nextCursor`.
      in: query
    
    documentId:
      name: documentId
      in: path
      required: true
      schema:
        type: string
        example: 12db1a0a
      description: The document's URL slug (e.g. `abc123`) or its canonical workbook UUID.
    sortDirection:
      name: sortDirection
      in: query
      required: false
      schema:
        type: string
        enum: [asc, desc]
      description: |
        Direction for sorting results:

        - `asc` - Ascending order (A-Z, 0-9)
        - `desc` - Descending order (Z-A, 9-0)
    pageSize:
      name: pageSize
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
        example: 20
      required: false
      description: Number of results per page
      in: query
    
    scheduleId:
      name: scheduleId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: |
        The ID of the schedule. To find a schedule's ID:

        1. Navigate to the dashboard or workbook.
        2. Click **File > Deliveries & Alerts**.
        3. Next to a schedule, click **Edit**.

        The schedule's ID is in the page's URL, after `/schedules/`. For example, the schedule ID in this URL is `123e4567-e89b-12d3-a456-426614174000`:

        ```markdown wrap
        https://blobsrus.omniapp.co/dashboards/e23ebaa0/schedules/123e4567-e89b-12d3-a456-426614174000
        ```
    
    modelId:
      name: modelId
      schema:
        type: string
        format: uuid
        example: 123e4567-e89b-12d3-a456-426614174000
      required: true
      in: path
      description: The ID of the model.
    colorPaletteId:
      name: colorPaletteId
      in: path
      required: true
      schema:
        type: string
        format: uuid
        example: 3c90c3cc-0d44-4b50-8888-8dd25736052a
      description: Palette ID
    topicName:
      name: topicName
      schema:
        type: string
        example: sales_analytics
      required: true
      description: The name of the topic.
      in: path
    userId:
      name: userId
      schema:
        type: string
        format: uuid
      required: false
      description: |
        **Organization API key required.** The membership ID of the user to act as.
      in: query

    userAttributeId:
      name: userAttributeId
      in: path
      required: true
      description: User attribute ID
      schema:
        type: string
        format: uuid
        example: a1b2c3d4-e5f6-7890-abcd-ef1234567890

    skillId:
      name: identifier
      schema:
        type: string
        minLength: 1
        maxLength: 48
        pattern: ^[a-z0-9][a-z0-9-]*$
        example: quarterly-report
      required: false
      description: Filter to skills holding this handle.
      in: query
  schemas:
    AiCreditControlsResponse:
      type: object
      required:
        - accountCreditLimit
        - creditsUsed
        - downgradeCredits
        - entityGroupDefaultCredits
        - periodEnd
        - periodStart
        - shutoffCredits
        - userDefaultCredits
      properties:
        accountCreditLimit:
          type: number
          minimum: 0
          description: Monthly AI credit limit for the whole Omni account, which is shared across every organization associated with the account. `0` when no limit is configured.
          example: 2000
        creditsUsed:
          type: number
          minimum: 0
          description: This organizations's credit usage for the current billing period.
          example: 450
        downgradeCredits:
          type: number
          nullable: true
          minimum: 0
          description: Downgrade threshold, or `null` if the downgrade control is off.
          example: 800
        entityGroupDefaultCredits:
          type: number
          nullable: true
          minimum: 0
          description: Default per-entity-group AI credit limit, or `null` when entity groups are unlimited by default.
          example: 100
        periodEnd:
          type: integer
          minimum: 0
          description: End of the current billing period as a Unix ms timestamp (UTC calendar-month boundary).
        periodStart:
          type: integer
          minimum: 0
          description: Start of the current billing period as a Unix ms timestamp (UTC calendar-month boundary).
        shutoffCredits:
          type: number
          nullable: true
          minimum: 0
          description: Shutoff threshold, or `null` if the shutoff control is off.
          example: 1200
        userDefaultCredits:
          type: number
          nullable: true
          minimum: 0
          description: Default per-user AI credit limit, or `null` when users are unlimited by default.
          example: 100

    IpAllowlist:
      type: object
      properties:
        allowsSupport:
          type: boolean
          description: Whether Omni support staff, signing in through Omni's own support login, can reach the organization from outside the list.
          example: false
        enabled:
          type: boolean
          description: Whether the allowlist is enforced. While false the entries are kept, but no address is blocked.
          example: true
        entries:
          type: array
          items:  # IpAllowlistEntry
            type: object
            properties:
              address:
                type: string
                description: 'A public IPv4 or IPv6 address or CIDR range, as stored: always network/prefix, so a single address reads 198.51.100.7/32.'
                example: 203.0.113.0/24
              note:
                type: string
                nullable: true
                description: What the entry is for, or null when it has no note.
                example: Boston VPN
            required:
              - address
              - note
          description: The allowed addresses and ranges, in the order given.
        exemptsScim:
          type: boolean
          description: Whether SCIM provisioning requests (everything under /api/scim/) are accepted from any address. They still need an organization API key.
          example: true
      required:
        - allowsSupport
        - enabled
        - entries
        - exemptsScim

    DocumentAbilities:
      type: object
      description: |
        Document-level ability values, as stored on the document. These settings control what actions users can perform on a document, and correspond to the toggles in the **Abilities** section of the document's settings.
      required:
        - canAnalyze
        - canDownload
        - canDrill
        - canDuplicate
        - canRequestAccess
        - canSaveSpreadsheets
        - canSchedule
        - canUpload
        - canUseDashboardAi
        - canUseTimezoneOverride
        - canViewWorkbook
        - requirePullRequestToPublish
      properties:
        canAnalyze:
          type: boolean
          description: Allow exploring from this document
        canDownload:
          type: boolean
          description: Allow downloading
        canDrill:
          type: boolean
          description: Allow drill-down
        canDuplicate:
          type: boolean
          description: Allow duplicating
        canRequestAccess:
          type: boolean
          description: Allow requesting access
        canSaveSpreadsheets:
          type: boolean
          description: Allow creating spreadsheets
        canSchedule:
          type: boolean
          description: Allow scheduling
        canUpload:
          type: boolean
          description: Allow uploads
        canUseDashboardAi:
          type: boolean
          description: Allow using dashboard AI
        canUseTimezoneOverride:
          type: boolean
          description: Allow timezone override
        canViewWorkbook:
          type: boolean
          description: Allow viewing workbook
        requirePullRequestToPublish:
          type: boolean
          description: Require pull request to publish changes

    DocumentsV2CreateBody:
      type: object
      required:
        - modelId
        - name
      additionalProperties: false
      properties:
        modelId:
          type: string
          format: uuid
          description: Base workbook model the document is built on — a SHARED model, or a SHARED_EXTENSION with `allowAsWorkbookBase = true`.
        name:
          type: string
          minLength: 1
          maxLength: 254
          description: Document name.
        identifier:
          type: string
          pattern: "^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$"
          description: |
            Identifier (URL slug) for the new document. Must be unique within the organization; auto-generated when omitted. Lowercase letters, numbers, hyphens, and underscores only; cannot start or end with a hyphen or underscore.
        description:
          type:
            - string
            - "null"
          description: Document description.
        folderId:
          type:
            - string
            - "null"
          format: uuid
          description: |
            The folder to create the document in; Editor permissions or higherer are required on this folder. Does not apply to Organization Admins.

            When omitted, defaults to the caller's personal **My documents**, which requires permission to save personal content or the request is rejected.
        summary:
          type: string
          minLength: 1
          maxLength: 255
          description: Caller-supplied note describing the create, written to the history audit trail. When omitted, Omni auto-fills it with `"Created document"`.
        queryPresentations:
          $ref: '#/components/schemas/DocumentsV2QueryPresentationsPatch'
        controls:
          $ref: '#/components/schemas/DocumentsV2ControlsPatch'
        settings:
          $ref: '#/components/schemas/DocumentsV2SettingsPatch'
        containers:
          $ref: '#/components/schemas/DocumentsV2Containers'
        app:
          $ref: '#/components/schemas/AppCreateBody'

    AiQuerySort:
      type: object
      properties:
        column_name:
          type: string
          description: Fully qualified field name to sort by (e.g., "view_name.field_name").
          example: order_items.total_revenue
        sort_descending:
          type: boolean
          description: Whether to sort in descending order.
          example: true
      required:
        - column_name
        - sort_descending

    AiLlmDescription:
      type: string
      description: |
        The LLM's description.
      example: Fast answers for quick lookups

    AiLlmModel:
      type: string
      minLength: 1
      maxLength: 512
      description: |
        The LLM's catalog name on an Omni-managed provider (e.g. `claude-sonnet-5`) or the LLM's ID if using [Bring your own key](/ai/settings/model-providers/index).
      example: claude-sonnet-5-5

    AiLlmProvider:
      type: string
      enum:
        - bedrock
        - anthropic
        - openai
        - grok
        - snowflake_cortex
        - openai_compatible
        - fireworks
        - fireworks_managed
        - databricks_genie
        - vertex_ai
      description: |
        The LLM provider. Use this value when passing `requestedLlm.provider`.
      example: bedrock

    AiLlmThinkingLevels:
      type: array
      items:
        type: string
        enum:
          - default
          - none
          - low
          - medium
          - high
      description: |
        The thinking levels this LLM supports, any of which may be passed as `requestedLlm.thinking`.
      example:
        - default
        - low
        - medium
        - high

    AiLlmIsDefault:
      type: boolean
      description: |
        Whether this is the LLM a job or query runs on when it doesn't name a `requestedLlm`.
      example: false

    AppCreateBody:
      # DocumentsV2CreateApp
      type: object
      required:
        - html
      additionalProperties: false
      description: |
        Creates an app document.
        
        Mutually exclusive with `containers` (including an explicit `null`), `controls`, and `settings` — combining `app` with any of these is a `400`. `queryPresentations` can still be sent alongside `app`.
      properties:
        html:
          type: string
          minLength: 1
          maxLength: 2097152
          description: The complete app HTML document, capped at 2 MiB of UTF-8 text.
        settings:
          $ref: '#/components/schemas/AppSettings'

    DocumentsV2CreateDraftBody:
      type: object
      additionalProperties: false
      description: The draft-route patch body plus an optional `branchId` selecting the branch the new draft is attached to.
      properties:
        branchId:
          type: string
          format: uuid
          description: Branch the draft is created on. Omit for a draft on the main (unpublished) workspace.
        modelId:
          type: string
          format: uuid
          description: Immutable model identifier. Must match the document's current base model or the API will return a 400 error.
        workbookModelId:
          type: string
          format: uuid
          description: Immutable model identifier. Must match the document's current workbook model or the API will return a 400 error.
        name:
          type: string
          minLength: 1
          maxLength: 254
          description: Document name.
        description:
          type:
            - string
            - "null"
          description: Document description.
        summary:
          type: string
          minLength: 1
          maxLength: 255
          description: Caller-supplied description of what this patch changes, written to the history audit trail. When omitted, Omni auto-generates one from the updated sections.
        queryPresentations:
          $ref: '#/components/schemas/DocumentsV2QueryPresentationsPatch'
        controls:
          $ref: '#/components/schemas/DocumentsV2ControlsPatch'
        settings:
          $ref: '#/components/schemas/DocumentsV2SettingsPatch'
        containers:
          allOf:
            - $ref: '#/components/schemas/DocumentsV2Containers'
            - description: Container layout. When present, fully replaces the existing layout.
        app:
          $ref: '#/components/schemas/DocumentsAppReadOnly'

    DocumentsV2QueryPresentationsPatch:
      type: object
      description: Query presentations (tabs/tiles) keyed by record key.
      properties:
        data:
          type: object
          description: |
            Tabs keyed by record key (a positive-integer string, e.g. `"1"`, `"2"`). Shallow-merged by key — omitted keys are untouched; set a key to `null` to delete that tab. Capped at 48 non-null entries per patch.
          additionalProperties:
            oneOf:
              - $ref: '#/components/schemas/DocumentsV2QueryPresentation'
              - type: "null"
        order:
          type: array
          description: Tab display order. When present, replaces the existing order.
          items:
            type: string
            pattern: "^[1-9][0-9]*$"

    DocumentsV2QueryPresentationsRead:
      type: object
      required:
        - data
        - order
      properties:
        data:
          type: object
          description: Tabs keyed by record key (a positive-integer string).
          additionalProperties:
            $ref: '#/components/schemas/DocumentsV2QueryPresentation'
        order:
          type: array
          description: Tab display order.
          items:
            type: string
            pattern: "^[1-9][0-9]*$"

    DocumentsV2QueryPresentation:
      type: object
      required:
        - type
      description: |
        A single tab/tile. The server owns internal tile identity — callers never send `miniUuid`; tiles are addressed by their record key in `queryPresentations.data`.
      properties:
        type:
          type: string
          enum:
            - blank
            - csv
            - query
            - dataset
            - spreadsheet
            - sql
            - dbt
            - query-view
            - linked
            - foreign
            - app
          description: |
            The query presentation type:

            - `query` is a semantic query
            - `sql` is raw SQL
            - `linked` reuses another tile's query (see `sourceQueryPresentationKey`)
            - `foreign` is a cross-model tab whose query runs against a shared model other than the document's own (see `foreignModelId`). A new tab may be created as `foreign`; an existing tab can't be converted to or from `foreign`.
        name:
          type: string
          minLength: 1
          maxLength: 144
          description: User-provided tab name.
        subTitle:
          type:
            - string
            - "null"
          maxLength: 250
          description: User-provided tab subtitle.
        description:
          type:
            - string
            - "null"
          maxLength: 500
          description: User-provided tab description.
        sourceQueryPresentationKey:
          type:
            - string
            - "null"
          pattern: "^[1-9][0-9]*$"
          description: |
            For `linked`-type tabs, the record key — the same identifier used as a `queryPresentations.data` key — of the source tile whose query this tab reuses. Null for all other tab types. This is a tile record key, **not** a positional index into `order`.
        topicName:
          type:
            - string
            - "null"
          description: The topic (explore) this query is built on.
        isSql:
          type:
            - boolean
            - "null"
          description: Whether this tab is in raw SQL mode.
        prefersChart:
          type: boolean
          description: When true, the chart view is shown by default instead of the data table.
        automaticVis:
          type:
            - boolean
            - "null"
          description: When true, the system automatically selects the best visualization type.
        filterOrder:
          type: array
          items:
            type: string
          description: Ordered list of filter field names controlling display order on this tab.
        editingModelObjectName:
          type:
            - string
            - "null"
          description: Model object (view/topic) currently being edited via the dataset/query-view editor. Applies only to `dataset` / `query-view` tabs — omitted from reads and rejected on patches for other tab types.
        editingModelObjectNameChange:
          type:
            - string
            - "null"
          description: Pending rename of the model object being edited. Applies only to `dataset` / `query-view` tabs — omitted from reads and rejected on patches for other tab types.
        fileUploadId:
          type:
            - string
            - "null"
          format: uuid
          description: The ID of the upload (from POST /v1/uploads) backing this tab's data. Applies only to `csv` and `spreadsheet` tabs — omitted from reads and rejected on patches for other tab types. On patch, providing it on a new tab key binds that upload as the tab's Source; changing it on an existing key repoints the tab at a different upload (regenerating the tab, same semantics as any other content change).
        query:
          type:
            - object
            - "null"
          additionalProperties: true
          description: The semantic query for this tab (fields, filters, sorts, limits, etc.), minus the server-owned workbook model anchors. Omitted on a no-op replay; the server anchors new/changed tiles to the draft.
        visConfig:
          type:
            - object
            - "null"
          additionalProperties: true
          description: Visualization configuration for the tile.
        resultConfig:
          type: object
          additionalProperties: true
          description: Result display configuration (column widths, frozen columns, conditional formatting, number formatting, etc.).
        aiConfig:
          type:
            - object
            - "null"
          additionalProperties: true
          description: AI-generated metadata config (subtitle/description auto-generation settings).
        model_extension_id:
          type:
            - string
            - "null"
          description: |
            Identifies the query model bound to this tile. Returned on read (omitted for `linked`-type tiles, whose query resolves from the source tile). This value is read-only and managed by Omni - any value provided in write requests will be ignored.
        foreignModelId:
          type:
            - string
            - "null"
          description: |
            Present only on `foreign` tabs: the shared model this tab's query runs against (a model other than the document's own). 
            
            Required when creating a new `foreign` tab, where it must name a [shared model you can build workbooks on](/analyze-explore/cross-model-tabs), other than the document's own base model. On an existing `foreign` tab this value is read-only — a supplied value is ignored and the tab keeps its stored execution model. Rejected on every other tab type.

    DocumentsV2ControlsPatch:
      type: object
      description: Dashboard filters/controls keyed by control ID.
      properties:
        data:
          type: object
          description: Controls keyed by control ID. Shallow-merged by key — omitted keys are untouched; set to `null` to delete.
          additionalProperties:
            oneOf:
              - $ref: '#/components/schemas/DocumentsV2Control'
              - type: "null"
        order:
          type: array
          description: Display order for controls. When present, replaces the existing order.
          items:
            type: string

    DocumentsV2ControlsRead:
      type: object
      required:
        - data
        - order
      properties:
        data:
          type: object
          description: Controls keyed by control ID.
          additionalProperties:
            $ref: '#/components/schemas/DocumentsV2Control'
        order:
          type: array
          description: Display order for controls.
          items:
            type: string

    DocumentsV2Control:
      type: object
      additionalProperties: true
      description: |
        A dashboard control (filter). The full config grammar — control type, field bindings, filter values, and (for multi-field filters) the recursive composite-filter grammar — is validated by the server on apply. See the dashboard filters & controls reference for the complete structure.

    DocumentsV2SettingsPatch:
      type: object
      description: Document settings. Shallow-merged with the existing settings.
      properties:
        controlBar:
          type: object
          additionalProperties: false
          description: Control bar presentation. Merged field by field — omitted fields keep their current value.
          properties:
            collapsible:
              type: boolean
              description: When `true`, filters that do not fit in a single row are hidden behind a toggle. When `false`, every filter is shown.
            defaultExpanded:
              type: boolean
              description: When `true`, a collapsible control bar starts expanded, showing every filter. Only meaningful when `collapsible` is `true`.
            fixed:
              type: boolean
              description: When `true`, the control bar stays at the top of the dashboard while scrolling.
        crossfilterEnabled:
          type: boolean
          description: When `true`, clicking a value in one tile filters all other tiles on the dashboard.
        customText:
          type:
            - object
            - "null"
          description: Custom text replacing default UI strings on the dashboard.
          properties:
            queryError:
              type: string
              description: Custom text shown when a query errors, replacing the default error text.
            queryNoResults:
              type: string
              description: Custom text shown when a query returns no results, replacing the default empty state.
        facetFilters:
          type: boolean
          description: When true, dashboard filters are applied per-facet when faceting is active.
        refreshInterval:
          type:
            - number
            - "null"
          description: Auto-refresh interval in seconds. Null disables auto-refresh.
        runQueriesOn:
          type:
            - string
            - "null"
          enum:
            - current-page
            - all-pages
            - null
          description: Controls whether dashboard queries execute on the visible page or across all pages.

    DocumentsV2SettingsRead:
      type: object
      required:
        - controlBar
        - crossfilterEnabled
        - customText
        - facetFilters
        - refreshInterval
        - runQueriesOn
      properties:
        controlBar:
          type: object
          required:
            - collapsible
            - defaultExpanded
            - fixed
          description: "Presentation of the control bar: collapsibility, initial expansion, and whether it stays fixed while scrolling."
          properties:
            collapsible:
              type: boolean
              description: When `true`, filters that do not fit in a single row are hidden behind a toggle. When `false`, every filter is shown.
            defaultExpanded:
              type: boolean
              description: When `true`, a collapsible control bar starts expanded, showing every filter. Only meaningful when `collapsible` is `true`.
            fixed:
              type: boolean
              description: When `true`, the control bar stays at the top of the dashboard while scrolling.
        crossfilterEnabled:
          type: boolean
          description: When `true`, clicking a value in one tile filters all other tiles on the dashboard.
        customText:
          type:
            - object
            - "null"
          description: Custom text replacing default UI strings on the dashboard.
          properties:
            queryError:
              type: string
            queryNoResults:
              type: string
        facetFilters:
          type: boolean
          description: When true, dashboard filters are applied per-facet when faceting is active.
        refreshInterval:
          type:
            - number
            - "null"
          description: Auto-refresh interval in seconds. Null disables auto-refresh.
        runQueriesOn:
          type:
            - string
            - "null"
          enum:
            - current-page
            - all-pages
            - null
          description: Controls whether dashboard queries execute on the visible page or across all pages.

    DocumentsV2Containers:
      type: array
      description: |
        Container layout array (grid / stack / page / reference containers, recursively nested). On a patch, when present this **fully replaces** the existing layout. The server validates the full structure on apply; container `instanceKey` / `referenceKey` values round-trip unchanged. See the dashboard layout reference for the complete grammar.
      items:
        type: object
        additionalProperties: true

    DocumentsApps:
      type: object
      required:
        - url
      additionalProperties: false
      description: |
        Present only on an app document. The app's HTML and settings live at the `url` sub-resource — the document body itself carries nothing app-scoped. Omitted entirely (the document reads as workbook-only) when the organization has apps disabled.
      properties:
        url:
          type: string
          description: |
            API path of the app sub-resource, where the app's HTML and settings are read and written. This sub-resource is an alpha surface and its shape may change without a deprecation cycle while apps mature; the document routes described here are stable.

    DocumentsAppReadOnly:
      type: object
      additionalProperties: false
      description: |
        **Currently read-only**. Will be used in future releases.
      properties:
        url:
          type: string

    DocumentsV2ReadResponse:
      type: object
      required:
        - description
        - name
        - queryPresentations
      description: |
        Current document or draft state. 
        
        Submit this as-is as a draft `PATCH` body. `containers`, `controls`, and `settings` are dashboard-scoped and omitted for workbook-only documents with no layout yet. An app document returns the workbook-scoped fields plus an `app` property; the `app` property is omitted when the organization has apps disabled.
      properties:
        name:
          type: string
          maxLength: 254
          description: Document name.
        description:
          type:
            - string
            - "null"
          description: Document description.
        modelId:
          type: string
          format: uuid
          description: |
            The `SHARED` or `SHARED_EXTENSION` model the workbook is built on.
        workbookModelId:
          type: string
          format: uuid
          description: |
            The workbook-layer model ID. Each document has its own workbook model for document-specific fields and calculations. For drafts, this returns the draft's workbook model ID, not the published document's.
        queryPresentations:
          $ref: '#/components/schemas/DocumentsV2QueryPresentationsRead'
        controls:
          $ref: '#/components/schemas/DocumentsV2ControlsRead'
        settings:
          $ref: '#/components/schemas/DocumentsV2SettingsRead'
        containers:
          $ref: '#/components/schemas/DocumentsV2Containers'
        app:
          $ref: '#/components/schemas/DocumentsApps'
        draftOf:
          type: object
          additionalProperties: false
          description: |
            Present only when the requested identifier names a draft. Omitted for published-document reads.
          properties:
            identifier:
              type: string
              description: The published document's identifier that this draft belongs to.

    AppHTMLSettings:
      type: object
      required:
        - html
        - settings
      description: |
        The published app's HTML and sandbox settings.
      properties:
        html:
          type: string
          description: The complete app HTML document.
        settings:
          $ref: '#/components/schemas/AppSettings'

    AppSettings:
      type: object
      additionalProperties: false
      description: |
        The app's sandbox settings. A write replaces the whole object; omitted fields use their locked-down defaults. Host lists are normalized (deduped, invalid or Omni-owned hosts dropped) before they are stored.
      properties:
        allowClipboard:
          type: boolean
          default: false
          description: Allow the app to read from and write to the user's clipboard.
        allowDefaultMapProviders:
          type: boolean
          default: false
          description: Allow the app to load Omni's default map tile providers.
        allowDownloads:
          type: boolean
          default: false
          description: Allow the app to trigger file downloads.
        allowExternalNavigation:
          type: boolean
          default: false
          description: Allow the app to navigate to domains outside the `navAllowedDomains` / `safeDomains` allowlists.
        allowFullscreen:
          type: boolean
          default: false
          description: Allow the app to enter full screen via `requestFullscreen()` (e.g. a "Present" button).
        allowInternalNavigation:
          type: boolean
          default: false
          description: Allow the app to navigate within the Omni instance.
        externalNavOpensInNewTab:
          type: boolean
          default: true
          description: When true, external navigation opens in a new tab instead of the current one.
        navAllowedDomains:
          type: array
          items:
            type: string
          default: []
          description: Domains the app is allowed to navigate to when `navAllowedDomainsEnabled` is true.
        navAllowedDomainsEnabled:
          type: boolean
          default: false
          description: When true, restrict navigation to the domains listed in `navAllowedDomains`.
        safeDomains:
          type: array
          items:
            type: string
          default: []
          description: Domains treated as safe for embedded content when `safeDomainsEnabled` is true.
        safeDomainsEnabled:
          type: boolean
          default: false
          description: When true, restrict embedded content to the domains listed in `safeDomains`.

    DocumentsV2CreateResponse:
      type: object
      required:
        - description
        - identifier
        - name
      properties:
        identifier:
          type: string
          description: Identifier of the newly created document.
        name:
          type: string
          description: Document name.
        description:
          type:
            - string
            - "null"
          description: Document description.
        warnings:
          $ref: '#/components/schemas/AppWarnings'

    DocumentsV2PatchDraftResponse:
      type: object
      required:
        - description
        - draftIdentifier
        - identifier
        - name
      properties:
        identifier:
          type: string
          description: Published document identifier the draft targets.
          example: "def456"
        draftIdentifier:
          type: string
          description: Identifier of the draft the patch was applied to.
          example: "abc123"
        name:
          type: string
          description: Document name.
          example: "Blob Sales"
        description:
          type:
            - string
            - "null"
          description: Document description.
          example: "Overview of daily Blobs R Us Sales"

    DocumentsV2PublishDraftResponse:
      type: object
      required:
        - description
        - identifier
        - name
      properties:
        identifier:
          type: string
          description: Published document identifier.
        name:
          type: string
          description: Document name.
        description:
          type:
            - string
            - "null"
          description: Document description.

    AppPutBody:
      type: object
      properties:
        html:
          type: string
          minLength: 1
          maxLength: 2097152
          description: The complete app HTML document. Replaces the current HTML; 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:
          allOf:
            - $ref: '#/components/schemas/AppSettings'
            - description: When present, replaces the app settings; omitted fields take their locked-down defaults. When absent, the current settings are retained.
      required:
        - html
      additionalProperties: false
    
    AppPutResponse:
      allOf:
        - $ref: '#/components/schemas/DocumentsV2PatchDraftResponse'
        - type: object
          properties:
            app:
              $ref: '#/components/schemas/AppState'
            warnings:
              $ref: '#/components/schemas/AppWarnings'
          required:
            - app
    
    AppHtmlEdit:
      # DocumentsV2HtmlEdit
      type: object
      required:
        - replace
        - search
      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 with enough surrounding context to be unique unless `replaceAll: true`.
      additionalProperties: false

    AppPatchBody:
      # DocumentsV2PatchAppBody
      type: object
      properties:
        htmlEdits:
          type: array
          items:
            $ref: '#/components/schemas/AppHtmlEdit'
          minItems: 1
          maxItems: 20
          description: |
            Targeted edits applied in order against the current HTML. A failed edit (not found, ambiguous, or result over the 2 MiB cap) rejects the entire request with 400 naming the edit. At most 20 edits per request.
        settings:
          allOf:
            - $ref: '#/components/schemas/AppSettings'
            - 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

    AppWriteResponse:
      # DocumentsV2AppWriteResponse
      allOf:
        - $ref: '#/components/schemas/DocumentsV2PatchDraftResponse'
        - type: object
          properties:
            app:
              $ref: '#/components/schemas/AppState'
            warnings:
              $ref: '#/components/schemas/AppWarnings'
          required:
            - app

    AppState:
      # DocumentsV2AppState
      type: object
      properties:
        settings:
          $ref: '#/components/schemas/AppSettings'
      required:
        - settings

    AiPickTopicBody:
      type: object
      required:
        - prompt
        - modelId
      properties:
        branchId:
          type: string
          description: Optional branch ID for the model. Must be a branch of the shared model specified by modelId.
          example: 550e8400-e29b-41d4-a716-446655440000
        currentTopicName:
          type: string
          description: Ignored by this endpoint. Pick topic always lets AI choose the topic.
          example: order_items
        modelId:
          type: string
          format: uuid
          description: |
            The UUID of the shared model to query against. Note that:
            
            - Only shared models are supported
            - The user must have **Querier** permissions or higher on the model
          example: 770e8400-e29b-41d4-a716-446655440002
        potentialTopicNames:
          type: array
          items:
            type: string
          description: Optional list of topic names to limit consideration to. If not provided, all topics the user has access to in the model will be evaluated.
          example:
            - order_items
            - customers
            - products
        prompt:
          type: string
          description: The natural language prompt to analyze. The AI will determine which topic best matches the data described in this prompt.
          example: How many orders were placed last month?
        userId:
          type: string
          format: uuid
          description: |
            User ID to evaluate topic access as. Their permissions will be used for permission-aware topic selection. AI credit checks and usage attribution also apply to this user (including their embed entity, if any) rather than the API key holder.

            **Only valid with Organization API keys.** PATs always act as the authenticated user.
          example: 990e8400-e29b-41d4-a716-446655440004

    AppWarnings:
      type: array
      items:
        type: string
      description: |
        Included only when non-blocking warnings are encountered when creating/updating an app. Currently, external resource hosts the app's iframe CSP will block until an Organization Admin allows them, and a map providers, printing and dialogs, full screen, clipboard copy, or downloads setting the organization's app policy turns off for every app. The write itself succeeded.

    AiPickTopicResponse:
      type: object
      required:
        - topicId
      properties:
        topicId:
          type: string
          description: |
            The name of the topic that best matches the prompt.

            Use this as the `topicName` parameter when calling the [Generate a query](/api/ai/generate-a-query) or the [Create AI job](/api/ai/create-ai-job) endpoints.
          example: order_items

    AiJobVisNonNullJsonValue:
      anyOf:
        - type: string
        - type: number
        - type: boolean
        - type: array
          items:
            oneOf:
              - $ref: '#/components/schemas/AiJobVisNonNullJsonValue'
              - type: 'null'
        - type: object
          additionalProperties:
            oneOf:
              - $ref: '#/components/schemas/AiJobVisNonNullJsonValue'
              - type: 'null'
    OmniQuery:
      type: object
      properties:
        aiGenerated:
          type: boolean
          description: True when AI generated this query’s SQL; the AI SQL is shown in the advanced SQL box.
        branch_id:
          type: string
          description: Branch model ID when querying against a model branch.
        calculations:
          type: array
          items:
            type: object
            properties:
              allow_refs_to_unselected_fields:
                type: boolean
                description: 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:
                type: integer
                exclusiveMinimum: 0
                description: 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:
                type: string
                description: Internal identifier for the calculation, used as the column alias.
              description:
                type: string
                description: Description of the calculation.
              format:
                type: string
                description: Number/date format string (e.g. "#,##0.00").
              label:
                type: string
                description: Display label shown in the UI.
              original_formula:
                type: string
                description: '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 on write and rejects the request if it does not parse.'
              outside_pivot:
                type: boolean
                description: When true, the calculation is evaluated outside the pivot grouping.
              pushdown:
                type: boolean
                description: Per-calc override for whether to evaluate before the row limit. `null` defers to the model-level default.
              sql:
                type: string
                description: Compiled SQL string produced from the formula.
              sql_expression:
                description: Parsed SQL expression tree (serialized, server-internal format). Omit it to have the server parse original_formula instead.
              swallow_errors:
                type: boolean
                default: true
                description: |
                  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. Defaults to `true` when omitted on write.
            required:
              - calc_name
            additionalProperties: {}
          description: Table calculations attached to this query.
        column_limit:
          type: number
          description: Max number of pivot columns to return.
        column_totals:
          type: object
          additionalProperties:
            type: object
            properties:
              type:
                type: string
                enum:
                  - aggregation
            required:
              - type
          description: Column-level aggregation totals, keyed by field name.
        controls:
          type: array
          items:
            oneOf:
              - type: object
                properties:
                  description:
                    type: string
                  hidden:
                    type: boolean
                  id:
                    type: string
                  label:
                    type: string
                  display:
                    type: string
                    enum:
                      - SELECT
                      - BUTTON_TOGGLE
                  field:
                    type: string
                  kind:
                    type: string
                    enum:
                      - FIELD
                      - TIMEFRAME
                  options:
                    type: array
                    items:
                      type: object
                      properties:
                        isDimension:
                          type: boolean
                        label:
                          type: string
                        topicLabel:
                          type: string
                        value:
                          type: string
                        viewLabel:
                          type: string
                      required:
                        - label
                        - value
                  type:
                    type: string
                    enum:
                      - FIELD_SELECTION
                required:
                  - id
                  - field
                  - kind
                  - options
                  - type
              - type: object
                properties:
                  description:
                    type: string
                  hidden:
                    type: boolean
                  id:
                    type: string
                  label:
                    type: string
                  display:
                    type: string
                    enum:
                      - SELECT
                      - BUTTON_TOGGLE
                  options:
                    type: array
                    items:
                      type: object
                      properties:
                        label:
                          type: string
                        value:
                          type: string
                      required:
                        - label
                        - value
                  selectionMap:
                    type: object
                    additionalProperties:
                      type: object
                      additionalProperties:
                        type: string
                  type:
                    type: string
                    enum:
                      - MULTI_FIELD_SELECTION
                  value:
                    type: string
                required:
                  - id
                  - options
                  - selectionMap
                  - type
              - type: object
                properties:
                  computations:
                    type: array
                    items:
                      type: object
                      properties:
                        filterId:
                          type: string
                        isDynamicPreviousPeriod:
                          type: boolean
                        periodsAgo:
                          type: number
                          nullable: true
                        timeUnitName:
                          type: string
                          nullable: true
                      required:
                        - periodsAgo
                        - timeUnitName
                  filterFieldName:
                    type: string
                  filterId:
                    type: string
                  id:
                    type: string
                  type:
                    type: string
                    enum:
                      - PERIOD_OVER_PERIOD
                required:
                  - computations
                  - filterFieldName
                  - id
                  - type
              - type: object
                properties:
                  description:
                    type: string
                  hidden:
                    type: boolean
                  id:
                    type: string
                  label:
                    type: string
                  fieldOrder:
                    type: string
                    enum:
                      - query
                      - control
                  options:
                    type: array
                    items:
                      type: object
                      properties:
                        isDimension:
                          type: boolean
                        label:
                          type: string
                        topicLabel:
                          type: string
                        value:
                          type: string
                        viewLabel:
                          type: string
                      required:
                        - label
                        - value
                  type:
                    type: string
                    enum:
                      - FIELD_PICKER
                  values:
                    type: array
                    items:
                      type: string
                required:
                  - id
                  - options
                  - type
              - type: object
                properties:
                  description:
                    type: string
                  hidden:
                    type: boolean
                  id:
                    type: string
                  label:
                    type: string
                  conjunction:
                    type: string
                    enum:
                      - OR
                  filters:
                    type: array
                    items:
                      type: object
                      properties:
                        fieldName:
                          type: string
                        filter:
                          $ref: '#/components/schemas/JsonValue'
                        id:
                          type: string
                      required:
                        - fieldName
                        - filter
                        - id
                  type:
                    type: string
                    enum:
                      - MULTI_FIELD_FILTER
                required:
                  - id
                  - conjunction
                  - filters
                  - type
              - type: object
                properties:
                  description:
                    type: string
                  hidden:
                    type: boolean
                  id:
                    type: string
                  label:
                    type: string
                  fieldSelection:
                    oneOf:
                      - type: object
                        properties:
                          mode:
                            type: string
                            enum:
                              - full-model
                        required:
                          - mode
                      - type: object
                        properties:
                          mode:
                            type: string
                            enum:
                              - auto
                          topics:
                            type: array
                            items:
                              type: string
                        required:
                          - mode
                          - topics
                      - type: object
                        properties:
                          fields:
                            type: array
                            items:
                              type: object
                              properties:
                                fieldName:
                                  type: string
                                topicName:
                                  type: string
                              required:
                                - fieldName
                          mode:
                            type: string
                            enum:
                              - specific
                        required:
                          - fields
                          - mode
                  includeViewNameInLabels:
                    type: boolean
                  type:
                    type: string
                    enum:
                      - DYNAMIC_FILTER
                required:
                  - id
                  - fieldSelection
                  - type
              - type: object
                properties:
                  description:
                    type: string
                  hidden:
                    type: boolean
                  id:
                    type: string
                  label:
                    type: string
                  defaultValue:
                    type: integer
                    minimum: 1
                  field:
                    type: string
                  max:
                    type: integer
                    minimum: 1
                  min:
                    type: integer
                    minimum: 1
                  type:
                    type: string
                    enum:
                      - TOP_N
                  value:
                    type: integer
                    minimum: 1
                required:
                  - id
                  - defaultValue
                  - field
                  - type
                  - value
          description: Interactive controls (field selectors, PoP controls) attached to this query.
        cube_metadata:
          type: object
          properties:
            cube_name:
              type: string
            hash_key:
              type: string
            topic_name:
              type: string
          required:
            - cube_name
            - hash_key
            - topic_name
          additionalProperties: {}
          description: Cube-specific metadata (topic name, cube name, hash key) for cube-backed queries.
        custom_summary_types:
          type: object
          additionalProperties:
            type: string
          description: Per-field custom summary functions (e.g. SUM, AVG, COUNT).
        dbtFileName:
          type: string
          description: dbt file name when this query is backed by a dbt model.
        dbtMode:
          type: boolean
          description: Whether this query is in dbt mode.
        default_group_by:
          type: boolean
          description: 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:
          type: number
          description: Index of the primary dimension used for result ordering.
        executableSQL:
          type: string
          description: Server-compiled SQL string (read-only, set by the backend).
        fields:
          type: array
          items:
            type: string
          description: Model field names selected for the query (dimensions + measures).
        fill_fields:
          type: array
          items:
            type: string
          description: Fields whose missing date/time values should be filled with nulls to create continuous series.
        filters:
          type: object
          additionalProperties:
            oneOf:
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  case_insensitive:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  kind:
                    type: string
                    enum:
                      - CONTAINS
                      - ENDS_WITH
                      - STARTS_WITH
                      - EQUALS
                      - IS_EMPTY
                      - SQL_LIKE
                  type:
                    type: string
                    enum:
                      - string
                  values:
                    type: array
                    items:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - kind
                  - type
                  - values
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  is_inclusive:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  kind:
                    type: string
                    enum:
                      - LESS_THAN
                      - GREATER_THAN
                      - EQUALS
                      - BETWEEN
                  type:
                    type: string
                    enum:
                      - number
                  values:
                    type: array
                    items:
                      anyOf:
                        - type: string
                        - type: number
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - kind
                  - type
                  - values
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  isFiscal:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  kind:
                    type: string
                    enum:
                      - IS_ON_DAY_OF_WEEK
                      - IS_ON_DAY_OF_QUARTER
                      - IS_IN_MONTH_OF_YEAR
                      - IS_ON_DAY_OF_YEAR
                      - IS_AT_HOUR_OF_DAY
                      - IS_IN_QUARTER_OF_YEAR
                      - IS_IN_WEEK_OF_YEAR
                      - IS_ON_DAY_OF_MONTH
                      - BETWEEN
                      - ON_OR_AFTER
                      - BEFORE
                      - TIME_FOR_INTERVAL_DURATION
                      - TIME_FOR_UNIT_DURATION
                      - QUERY_OFFSET
                  left_side:
                    type: string
                    nullable: true
                  offset_interval_string:
                    type: string
                    nullable: true
                  right_side:
                    type: string
                    nullable: true
                  type:
                    type: string
                    enum:
                      - date
                  ui_type:
                    type: string
                    nullable: true
                    enum:
                      - ANY_TIME
                      - BEFORE
                      - BETWEEN
                      - MONTH_OF_YEAR
                      - PAST
                      - YEAR
                      - DAY
                      - IS_ON_DAY_OF_WEEK
                      - ON_OR_AFTER
                      - IS_IN_THE_MONTH
                      - IS_IN_THE_QUARTER
                      - IS_IN_THE_FISCAL_QUARTER
                      - IS_IN_THE_FISCAL_YEAR
                      - TIME_FOR_INTERVAL_DURATION
                      - TIME_FOR_UNIT_DURATION
                      - CUSTOM
                      - null
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - kind
                  - type
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  type:
                    type: string
                    enum:
                      - 'null'
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - type
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  treat_nulls_as_false:
                    type: boolean
                  type:
                    type: string
                    enum:
                      - boolean
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - type
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  disregard_limit:
                    type: boolean
                  field_name:
                    type: string
                  is_negative:
                    type: boolean
                    nullable: true
                  query_id:
                    type: string
                  type:
                    type: string
                    enum:
                      - query
                  view_query:
                    type: object
                    properties:
                      fields:
                        type: array
                        items:
                          type: string
                      filters:
                        type: object
                        additionalProperties: {}
                        default: {}
                      limit:
                        type: number
                      sorts:
                        type: array
                        items: {}
                        default: []
                      table:
                        type: string
                    required:
                      - fields
                    additionalProperties: {}
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - type
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  type:
                    type: string
                    enum:
                      - user_attribute
                  user_attribute_name:
                    type: string
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - type
                  - user_attribute_name
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  conjunction:
                    type: string
                    enum:
                      - OR
                      - AND
                  filters:
                    type: array
                    items:
                      $ref: '#/components/schemas/CompositeFilter'
                  is_negative:
                    type: boolean
                    nullable: true
                  type:
                    type: string
                    enum:
                      - composite
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - conjunction
                  - filters
                  - type
                additionalProperties: {}
          description: Query filters keyed by filter ID.
        filtersUsedInSql:
          type: object
          additionalProperties:
            oneOf:
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  case_insensitive:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  kind:
                    type: string
                    enum:
                      - CONTAINS
                      - ENDS_WITH
                      - STARTS_WITH
                      - EQUALS
                      - IS_EMPTY
                      - SQL_LIKE
                  type:
                    type: string
                    enum:
                      - string
                  values:
                    type: array
                    items:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - kind
                  - type
                  - values
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  is_inclusive:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  kind:
                    type: string
                    enum:
                      - LESS_THAN
                      - GREATER_THAN
                      - EQUALS
                      - BETWEEN
                  type:
                    type: string
                    enum:
                      - number
                  values:
                    type: array
                    items:
                      anyOf:
                        - type: string
                        - type: number
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - kind
                  - type
                  - values
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  isFiscal:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  kind:
                    type: string
                    enum:
                      - IS_ON_DAY_OF_WEEK
                      - IS_ON_DAY_OF_QUARTER
                      - IS_IN_MONTH_OF_YEAR
                      - IS_ON_DAY_OF_YEAR
                      - IS_AT_HOUR_OF_DAY
                      - IS_IN_QUARTER_OF_YEAR
                      - IS_IN_WEEK_OF_YEAR
                      - IS_ON_DAY_OF_MONTH
                      - BETWEEN
                      - ON_OR_AFTER
                      - BEFORE
                      - TIME_FOR_INTERVAL_DURATION
                      - TIME_FOR_UNIT_DURATION
                      - QUERY_OFFSET
                  left_side:
                    type: string
                    nullable: true
                  offset_interval_string:
                    type: string
                    nullable: true
                  right_side:
                    type: string
                    nullable: true
                  type:
                    type: string
                    enum:
                      - date
                  ui_type:
                    type: string
                    nullable: true
                    enum:
                      - ANY_TIME
                      - BEFORE
                      - BETWEEN
                      - MONTH_OF_YEAR
                      - PAST
                      - YEAR
                      - DAY
                      - IS_ON_DAY_OF_WEEK
                      - ON_OR_AFTER
                      - IS_IN_THE_MONTH
                      - IS_IN_THE_QUARTER
                      - IS_IN_THE_FISCAL_QUARTER
                      - IS_IN_THE_FISCAL_YEAR
                      - TIME_FOR_INTERVAL_DURATION
                      - TIME_FOR_UNIT_DURATION
                      - CUSTOM
                      - null
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - kind
                  - type
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  type:
                    type: string
                    enum:
                      - 'null'
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - type
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  treat_nulls_as_false:
                    type: boolean
                  type:
                    type: string
                    enum:
                      - boolean
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - type
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  disregard_limit:
                    type: boolean
                  field_name:
                    type: string
                  is_negative:
                    type: boolean
                    nullable: true
                  query_id:
                    type: string
                  type:
                    type: string
                    enum:
                      - query
                  view_query:
                    type: object
                    properties:
                      fields:
                        type: array
                        items:
                          type: string
                      filters:
                        type: object
                        additionalProperties: {}
                        default: {}
                      limit:
                        type: number
                      sorts:
                        type: array
                        items: {}
                        default: []
                      table:
                        type: string
                    required:
                      - fields
                    additionalProperties: {}
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - type
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  type:
                    type: string
                    enum:
                      - user_attribute
                  user_attribute_name:
                    type: string
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - type
                  - user_attribute_name
                additionalProperties: {}
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  conjunction:
                    type: string
                    enum:
                      - OR
                      - AND
                  filters:
                    type: array
                    items:
                      $ref: '#/components/schemas/CompositeFilter'
                  is_negative:
                    type: boolean
                    nullable: true
                  type:
                    type: string
                    enum:
                      - composite
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  base_view:
                    type: string
                  description:
                    type: string
                  fieldName:
                    type: string
                  filterControlType:
                    anyOf:
                      - type: string
                        enum:
                          - multiValueEquals
                          - singleValueEquals
                      - type: string
                        enum:
                          - singleDay
                          - timeframe
                  hidden:
                    type: boolean
                  label:
                    type: string
                  required:
                    type: boolean
                  requiredScope:
                    type: string
                    enum:
                      - dashboard
                      - tiles
                  topic:
                    type: string
                  watchedContainerIds:
                    type: array
                    items:
                      type: string
                required:
                  - conjunction
                  - filters
                  - type
                additionalProperties: {}
          description: Filters that were embedded directly in raw SQL (read-only).
        imported_model_ids:
          type: array
          items:
            type: string
          description: 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:
          type: string
          description: Name of the topic the query runs on; it also sets join path precedence.
        join_via_map:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: Virtual topic join path overrides, keyed by view name to ordered join path.
        limit:
          type: number
          description: Row limit for the query.
        manualSort:
          type: boolean
          description: When true, the user has explicitly set a custom sort order.
        metadata:
          type: object
          additionalProperties:
            type: object
            properties:
              format:
                type: string
              label:
                type: string
              order:
                type: number
            additionalProperties: {}
          description: Per-field display metadata (format, label, order).
        modelId:
          type: string
          description: The model ID this query runs against.
        model_extension_id:
          type: string
          description: ID of a model extension layered on top of the base model.
        offset:
          type: number
          description: Row offset for pagination.
        parsed:
          type: boolean
          description: Whether this raw SQL query has been parsed into a semantic query.
        periodOverPeriodTransposed:
          type: boolean
          description: Whether the period-over-period comparison columns are transposed.
        period_over_period_computations:
          type: array
          items:
            type: object
            properties:
              date_filter_field_name:
                type: string
                description: The date dimension field this comparison is anchored to.
              date_filter_id:
                type: string
                description: ID of the date filter being offset (if backed by a dashboard filter).
              is_dynamic_previous_period:
                type: boolean
                description: When true, the previous period is calculated dynamically relative to the current filter range.
              periods_ago:
                type: number
                nullable: true
                description: How many periods back to compare (e.g. 1 = previous period). Null if not set.
              time_unit_name:
                type: string
                nullable: true
                description: Time grain for the offset (e.g. "month", "year"). Null if not set.
            required:
              - date_filter_field_name
              - periods_ago
              - time_unit_name
            additionalProperties: {}
          description: Period-over-period comparison configurations.
        pivots:
          type: array
          items:
            type: string
          description: Field names to pivot on (columns become values).
        rewriteSql:
          type: boolean
          description: When true, the backend should rewrite/optimize the SQL.
        row_totals:
          type: object
          additionalProperties:
            type: object
            properties:
              type:
                type: string
                enum:
                  - aggregation
            required:
              - type
          description: Row-level aggregation totals, keyed by field name.
        sorts:
          type: array
          items:
            type: object
            properties:
              column_name:
                type: string
                description: The model field name to sort by.
              is_column_sort:
                type: boolean
                description: When true, this sort targets a pivoted column rather than a row dimension.
              pivot_value_map:
                type: object
                additionalProperties:
                  type: string
                  nullable: true
                description: Maps pivot field names to specific pivot values, scoping the sort to a single pivot column.
              sort_descending:
                type: boolean
                description: When true, sort order is descending.
              subtotal_sort:
                type: string
                description: Field name whose subtotal row should be used as the sort key.
            required:
              - column_name
              - sort_descending
            additionalProperties: {}
          description: Sort clauses applied to the query result set.
        sqlSortsEnabled:
          type: boolean
          description: Whether user-created sorts are enabled on a raw SQL query.
        staticQueryReferences:
          type: object
          additionalProperties: {}
          description: '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:
          type: string
          description: Base view (table) name in the model.
        transposed_measures:
          type: array
          items:
            type: string
          description: Measure field names that have been transposed into rows.
        userEditedSQL:
          type: string
          description: User-authored raw SQL (empty string when not in SQL mode).
        version:
          type: number
          description: Schema version for migration support.
      required:
        - calculations
        - column_totals
        - fields
        - fill_fields
        - filters
        - modelId
        - pivots
        - row_totals
        - sorts
        - table
        - userEditedSQL
      additionalProperties: {}
    JsonValue:
      anyOf:
        - type: string
        - type: number
        - type: boolean
        - type: 'null'
        - type: object
          additionalProperties:
            oneOf:
              - $ref: '#/components/schemas/JsonValue'
              - type: 'null'
        - type: array
          items:
            oneOf:
              - $ref: '#/components/schemas/JsonValue'
              - type: 'null'
      description: Arbitrary JSON value (string, number, boolean, null, object, or array).
    CompositeFilter:
      type: object
      properties:
        cancel_query_filter:
          type: boolean
        exclude_from_bind_all:
          type: boolean
        ignore_if_unjoinable:
          type: boolean
        conjunction:
          type: string
          enum:
            - OR
            - AND
        filters:
          type: array
          items:
            anyOf:
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  appliedLabels:
                    type: object
                    additionalProperties:
                      type: string
                  case_insensitive:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  kind:
                    type: string
                    enum:
                      - CONTAINS
                      - ENDS_WITH
                      - STARTS_WITH
                      - EQUALS
                      - IS_EMPTY
                      - SQL_LIKE
                  type:
                    type: string
                    enum:
                      - string
                  values:
                    type: array
                    items:
                      type: string
                required:
                  - kind
                  - type
                  - values
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  is_inclusive:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  kind:
                    type: string
                    enum:
                      - LESS_THAN
                      - GREATER_THAN
                      - EQUALS
                      - BETWEEN
                  type:
                    type: string
                    enum:
                      - number
                  values:
                    type: array
                    items:
                      anyOf:
                        - type: string
                        - type: number
                required:
                  - kind
                  - type
                  - values
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  isFiscal:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  kind:
                    type: string
                    enum:
                      - IS_ON_DAY_OF_WEEK
                      - IS_ON_DAY_OF_QUARTER
                      - IS_IN_MONTH_OF_YEAR
                      - IS_ON_DAY_OF_YEAR
                      - IS_AT_HOUR_OF_DAY
                      - IS_IN_QUARTER_OF_YEAR
                      - IS_IN_WEEK_OF_YEAR
                      - IS_ON_DAY_OF_MONTH
                      - BETWEEN
                      - ON_OR_AFTER
                      - BEFORE
                      - TIME_FOR_INTERVAL_DURATION
                      - TIME_FOR_UNIT_DURATION
                      - QUERY_OFFSET
                  left_side:
                    type: string
                    nullable: true
                  offset_interval_string:
                    type: string
                    nullable: true
                  right_side:
                    type: string
                    nullable: true
                  type:
                    type: string
                    enum:
                      - date
                  ui_type:
                    type: string
                    nullable: true
                    enum:
                      - ANY_TIME
                      - BEFORE
                      - BETWEEN
                      - MONTH_OF_YEAR
                      - PAST
                      - YEAR
                      - DAY
                      - IS_ON_DAY_OF_WEEK
                      - ON_OR_AFTER
                      - IS_IN_THE_MONTH
                      - IS_IN_THE_QUARTER
                      - IS_IN_THE_FISCAL_QUARTER
                      - IS_IN_THE_FISCAL_YEAR
                      - TIME_FOR_INTERVAL_DURATION
                      - TIME_FOR_UNIT_DURATION
                      - CUSTOM
                      - null
                required:
                  - kind
                  - type
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  type:
                    type: string
                    enum:
                      - 'null'
                required:
                  - type
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  is_negative:
                    type: boolean
                    nullable: true
                  treat_nulls_as_false:
                    type: boolean
                  type:
                    type: string
                    enum:
                      - boolean
                required:
                  - type
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  disregard_limit:
                    type: boolean
                  field_name:
                    type: string
                  is_negative:
                    type: boolean
                    nullable: true
                  query_id:
                    type: string
                  type:
                    type: string
                    enum:
                      - query
                  view_query:
                    type: object
                    properties:
                      fields:
                        type: array
                        items:
                          type: string
                      filters:
                        type: object
                        additionalProperties: {}
                        default: {}
                      limit:
                        type: number
                      sorts:
                        type: array
                        items: {}
                        default: []
                      table:
                        type: string
                    required:
                      - fields
                    additionalProperties: {}
                required:
                  - type
              - type: object
                properties:
                  cancel_query_filter:
                    type: boolean
                  exclude_from_bind_all:
                    type: boolean
                  ignore_if_unjoinable:
                    type: boolean
                  type:
                    type: string
                    enum:
                      - user_attribute
                  user_attribute_name:
                    type: string
                required:
                  - type
                  - user_attribute_name
              - $ref: '#/components/schemas/CompositeFilter'
          description: Child filters — each a simple filter or another composite filter. Recursive; see the dashboard-filters reference for the full grammar.
        is_negative:
          type: boolean
          nullable: true
        type:
          type: string
          enum:
            - composite
      required:
        - conjunction
        - filters
        - type

    Dialect:
      type: string
      description: |
        The database dialect.
      enum:
        - athena
        - bigquery
        - clickhouse
        - databricks
        - databricks_lakebase
        - exasol
        - mariadb
        - motherduck
        - mssql
        - mysql
        - oracle
        - postgres
        - redshift
        - sap_hana
        - snowflake
        - starrocks
        - trino

    SuccessResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true

    SchemaRefreshSchedule:
      type: object
      properties:
        scheduleId:
          type: string
          format: uuid
          description: The unique identifier of the schema refresh schedule
        connectionId:
          type: string
          format: uuid
          description: The unique identifier of the connection this schedule belongs to
        schedule:
          type: string
          description: |
            A 6-field cron expression in AWS EventBridge format defining when the schema refresh should run.

            Format: `minute hour day-of-month month day-of-week year`
        timezone:
          type: string
          description: The IANA timezone identifier for when the schedule should run
        description:
          type: string
          description: A human-readable description of the schedule, automatically generated from the cron expression and timezone
        hardRefresh:
          type: boolean
          default: false
          description: Whether the scheduled refresh performs a hard refresh (removes dropped objects) or soft refresh (additive only). Defaults to false (soft refresh).
        createdAt:
          type: string
          format: date-time
          description: The timestamp when the schedule was created
        updatedAt:
          type: string
          format: date-time
          description: The timestamp when the schedule was last updated
        disabledAt:
          type: string
          format: date-time
          nullable: true
          description: The timestamp when the schedule was disabled, or `null` if active

    DbtEnvironment:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier of the dbt environment
        name:
          type: string
          description: The name of the dbt environment
        isDefaultEnvironment:
          type: boolean
          description: Whether this is the default environment
        isDeferralEnabled:
          type: boolean
          description: |
            Whether deferral is enabled for this environment. Ignored (forced to `false`) for the default (production) environment.
        ownerId:
          type: string
          format: uuid
          nullable: true
          description: The organization membership ID of the owner of the dbt environment, or null
        targetDatabase:
          type: string
          nullable: true
          description: Target database override
        targetName:
          type: string
          nullable: true
          description: Target name override
        targetRole:
          type: string
          nullable: true
          description: Target role override
        targetSchema:
          type: string
          description: Target schema for the environment
        variables:
          type: array
          description: Environment variables
          items:
            type: object
            properties:
              id:
                type: string
                format: uuid
                description: Variable identifier
              name:
                type: string
                description: Variable name
              value:
                type: string
                nullable: true
                description: |
                  Variable value. If `isSecret: true`, this value will be `null` in responses to protect sensitive information.
              isSecret:
                type: boolean
                description: |
                  Whether the variable value is secret.

    Error:
      type: object
      properties:
        error:
          type: string
          description: HTTP response code for the error
          example: "<response_code>"
        message:
          type: string
          description: Detailed error description
          example: "<error_reason>"

    DocumentsUpgradeLayoutBody:
      type: object
      properties:
        clearExistingDraft:
          type: boolean
          default: false
          description: When upgrading a published document, discard any existing draft instead of failing with a conflict.
    DocumentsUpgradeLayoutResponse:
      type: object
      properties:
        identifier:
          type: string
          description: Document identifier
        upgraded:
          type: boolean
          description: True when the layout was upgraded, false when the document already had advanced layout (no-op).
      required:
        - identifier
        - upgraded

    ColorPaletteObject:
      type: object
      properties:
        colors:
          $ref: '#/components/schemas/ColorPaletteColors'
        id:
          $ref: '#/components/schemas/ColorPaletteId'
        created_at:
          $ref: '#/components/schemas/ColorPaletteCreatedAt'
        name:
          $ref: '#/components/schemas/ColorPaletteName'
        type:
          $ref: '#/components/schemas/ColorPaletteType'
        updated_at:
          $ref: '#/components/schemas/ColorPaletteUpdatedAt'
      example:
        id: 3c90c3cc-0d44-4b50-8888-8dd25736052a
        name: Brand colors
        type: discrete
        colors:
          - "#1f77b4"
          - "#ff7f0e"
          - "#2ca02c"
        created_at: "2026-09-10T10:00:00.000Z"
        updated_at: "2026-09-10T10:00:00.000Z"

    ColorPaletteColors:
      type: array
      items:
        type: string
        pattern: ^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
        example: '#1f77b4'
      minItems: 1
      maxItems: 50
      description: Ordered hex colors. Discrete palettes are used in order; continuous palettes interpolate between them.
      example:
        - '#1f77b4'
        - '#ff7f0e'
        - '#2ca02c'

    ColorPaletteName:
      type: string
      minLength: 1
      maxLength: 256
      description: Palette name, unique per type within the organization
      example: Brand colors

    ColorPaletteType:
      type: string
      enum:
        - discrete
        - continuous
      description: |
        - `discrete` palettes color categories
        - `continuous` palettes define a gradient for numeric scales
      example: discrete

    ColorPaletteId:
      type: string
      format: uuid
      description: Palette ID. Visualizations reference the palette by this value.

    ColorPaletteCreatedAt:
      type: string
      format: date-time
      description: When the palette was created

    ColorPaletteUpdatedAt:
      type: string
      format: date-time
      description: When the palette was last updated
  
    ColorPaletteExport:
      type: object
      properties:
        colors:
          type: array
          items:
            type: string
          description: Ordered colors as stored, usually hex
        name:
          type: string
          maxLength: 256
          description: Palette name
        type:
          type: string
          enum:
            - discrete
            - continuous
          description: |
            `discrete` or `continuous`
      required:
        - colors
        - name
        - type

    ImportedColorPalette:
      type: object
      required:
        - id
        - key
        - name
        - type
      properties:
        id:
          type: string
          format: uuid
          description: Palette ID in this organization
        key:
          type: string
          description: Export-local key, for example `export-palette-1`
        name:
          type: string
          description: Palette name
        type:
          type: string
          enum:
            - discrete
            - continuous
          description: '''discrete'' or ''continuous'''

    GridContainer:
      type: object
      properties:
        description:
          type: string
          description: Optional description for the container, providing additional context or information
        instanceKey:
          type: string
          description: Unique identifier for this container. Used to reference the container when adding, moving, or removing children.
        name:
          type: string
          description: Human-readable name for the container, used for easier reference in logic and design
        aspectRatio:
          type: string
        fillSpace:
          type: boolean
        height:
          type: string
        maxHeight:
          type: string
        maxWidth:
          type: string
        minHeight:
          type: string
        minWidth:
          type: string
        width:
          type: string
        after:
          anyOf:
            - $ref: '#/components/schemas/StackContainer'
            - $ref: '#/components/schemas/ReferenceContainer'
        before:
          anyOf:
            - $ref: '#/components/schemas/StackContainer'
            - $ref: '#/components/schemas/ReferenceContainer'
        children:
          type: array
          items:
            anyOf:
              - allOf:
                  - $ref: '#/components/schemas/ReferenceContainer'
                  - type: object
                    properties:
                      gridPosition:
                        type: object
                        properties:
                          h:
                            type: number
                          w:
                            type: number
                          x:
                            type: number
                          y:
                            type: number
                        required:
                          - h
                          - w
                          - x
                          - y
                    required:
                      - gridPosition
              - allOf:
                  - $ref: '#/components/schemas/StackContainer'
                  - type: object
                    properties:
                      gridPosition:
                        type: object
                        properties:
                          h:
                            type: number
                          w:
                            type: number
                          x:
                            type: number
                          y:
                            type: number
                        required:
                          - h
                          - w
                          - x
                          - y
                    required:
                      - gridPosition
              - allOf:
                  - $ref: '#/components/schemas/GridContainer'
                  - type: object
                    properties:
                      gridPosition:
                        type: object
                        properties:
                          h:
                            type: number
                          w:
                            type: number
                          x:
                            type: number
                          y:
                            type: number
                        required:
                          - h
                          - w
                          - x
                          - y
                    required:
                      - gridPosition
              - allOf:
                  - anyOf:
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                            description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                          padding:
                            anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: 'null'
                          preset:
                            type: string
                            pattern: ^[a-z0-9-]+$
                            description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                          as:
                            type: string
                            enum:
                              - chart
                              - result
                              - ai
                              - metadata
                            description: 'Render mode: "chart" for visualization, "result" for data table, "ai" for AI summary, "metadata" for query metadata'
                          detachedContent:
                            type: string
                            description: 'Set when a metadata field is detached: local text rendered instead of the query value'
                          format:
                            type: string
                            enum:
                              - subtitle
                              - description
                              - name
                            description: 'Display format when as is "ai" or "metadata": "name" (metadata only), "subtitle", or "description"'
                          id:
                            type: string
                            pattern: ^[1-9][0-9]*$
                            description: The query presentation ID this content item references
                          name:
                            type: string
                            description: Display name for this query item
                          style:
                            type: object
                            properties:
                              aspectRatio:
                                type: string
                              fillSpace:
                                type: boolean
                              height:
                                type: string
                              maxHeight:
                                type: string
                              maxWidth:
                                type: string
                              minHeight:
                                type: string
                              minWidth:
                                type: string
                              width:
                                type: string
                          type:
                            type: string
                            enum:
                              - query
                        required:
                          - instanceKey
                          - id
                          - type
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                            description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                          padding:
                            anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: 'null'
                          preset:
                            type: string
                            pattern: ^[a-z0-9-]+$
                            description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                          appearance:
                            oneOf:
                              - type: object
                                properties:
                                  as:
                                    type: string
                                    enum:
                                      - inline
                                required:
                                  - as
                              - type: object
                                properties:
                                  as:
                                    type: string
                                    enum:
                                      - tooltip
                                required:
                                  - as
                            description: 'Display mode: "inline" renders directly, "tooltip" shows on hover'
                          content:
                            type: string
                            description: The text content to display (supports markdown)
                          name:
                            type: string
                            description: Display name for this text item (e.g., title, subtitle)
                          style:
                            type: object
                            properties:
                              aspectRatio:
                                type: string
                              fillSpace:
                                type: boolean
                              height:
                                type: string
                              maxHeight:
                                type: string
                              maxWidth:
                                type: string
                              minHeight:
                                type: string
                              minWidth:
                                type: string
                              width:
                                type: string
                          textAlign:
                            type: string
                            enum:
                              - start
                              - center
                              - end
                            description: 'Text alignment: "start", "center", or "end"'
                          type:
                            type: string
                            enum:
                              - inline-text
                        required:
                          - instanceKey
                          - content
                          - type
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                            description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                          padding:
                            anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: 'null'
                          preset:
                            type: string
                            pattern: ^[a-z0-9-]+$
                            description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                          appearance:
                            oneOf:
                              - type: object
                                properties:
                                  as:
                                    type: string
                                    enum:
                                      - tabs
                                  variant:
                                    type: string
                                    enum:
                                      - underline
                                      - bordered
                                required:
                                  - as
                              - type: object
                                properties:
                                  as:
                                    type: string
                                    enum:
                                      - buttons
                                  variant:
                                    type: string
                                    enum:
                                      - segment
                                      - toggle
                                      - pills
                                required:
                                  - as
                              - type: object
                                properties:
                                  as:
                                    type: string
                                    enum:
                                      - list
                                  description:
                                    type: boolean
                                required:
                                  - as
                              - type: object
                                properties:
                                  as:
                                    type: string
                                    enum:
                                      - dropdown
                                  description:
                                    type: boolean
                                required:
                                  - as
                          options:
                            type: array
                            items:
                              type: object
                              properties:
                                disabled:
                                  type: boolean
                                id:
                                  type: string
                                includeControls:
                                  type: boolean
                                label:
                                  type: string
                                target:
                                  type: string
                                uri:
                                  type: string
                                value:
                                  type: string
                                  description: Page link target. Must exactly equal an existing page container’s instanceKey — not its name, label, or a slug. A value matching no page redirects to the main dashboard view with a "Page not found" message. Ignored when `uri` is set.
                            description: Omit to auto-derive one tab per page, kept in sync as pages are added, removed, renamed, or reordered. Set to curate, reorder, or add custom-URL tabs; each option’s `value` must then match a page instanceKey.
                          style:
                            type: object
                            properties:
                              aspectRatio:
                                type: string
                              fillSpace:
                                type: boolean
                              height:
                                type: string
                              maxHeight:
                                type: string
                              maxWidth:
                                type: string
                              minHeight:
                                type: string
                              minWidth:
                                type: string
                              width:
                                type: string
                          type:
                            type: string
                            enum:
                              - inline-page-switcher
                        required:
                          - instanceKey
                          - type
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                            description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                          padding:
                            anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: 'null'
                          preset:
                            type: string
                            pattern: ^[a-z0-9-]+$
                            description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                          id:
                            type: string
                            format: uuid
                            description: UUID of the rich text content block
                          style:
                            type: object
                            properties:
                              aspectRatio:
                                type: string
                              fillSpace:
                                type: boolean
                              height:
                                type: string
                              maxHeight:
                                type: string
                              maxWidth:
                                type: string
                              minHeight:
                                type: string
                              minWidth:
                                type: string
                              width:
                                type: string
                          type:
                            type: string
                            enum:
                              - text
                        required:
                          - instanceKey
                          - id
                          - type
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                            description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                          padding:
                            anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: 'null'
                          preset:
                            type: string
                            pattern: ^[a-z0-9-]+$
                            description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                          id:
                            type: string
                          type:
                            type: string
                            enum:
                              - control
                          style:
                            type: object
                            properties:
                              aspectRatio:
                                type: string
                              fillSpace:
                                type: boolean
                              height:
                                type: string
                              maxHeight:
                                type: string
                              maxWidth:
                                type: string
                              minHeight:
                                type: string
                              minWidth:
                                type: string
                              width:
                                type: string
                        required:
                          - instanceKey
                          - id
                          - type
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                            description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                          padding:
                            anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: 'null'
                          preset:
                            type: string
                            pattern: ^[a-z0-9-]+$
                            description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                          id:
                            type: string
                          type:
                            type: string
                            enum:
                              - filter
                          appearance:
                            type: object
                            properties:
                              control:
                                type: string
                                enum:
                                  - buttonToggle
                                  - dropdown
                              display:
                                type: string
                                enum:
                                  - inline
                                  - popover
                              options:
                                type: array
                                items:
                                  type: object
                                  properties:
                                    label:
                                      type: string
                                    value:
                                      type: string
                                  required:
                                    - value
                              value:
                                type: array
                                items:
                                  type: string
                          style:
                            type: object
                            properties:
                              aspectRatio:
                                type: string
                              fillSpace:
                                type: boolean
                              height:
                                type: string
                              maxHeight:
                                type: string
                              maxWidth:
                                type: string
                              minHeight:
                                type: string
                              minWidth:
                                type: string
                              width:
                                type: string
                        required:
                          - instanceKey
                          - id
                          - type
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                            description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                          padding:
                            anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: 'null'
                          preset:
                            type: string
                            pattern: ^[a-z0-9-]+$
                            description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                          size:
                            type: number
                            minimum: 0
                            maximum: 2000
                            description: Fixed size in px along the parent stack axis (default 16). In grids the size comes from gridPosition; ignored when style.fillSpace is true.
                          style:
                            type: object
                            properties:
                              aspectRatio:
                                type: string
                              fillSpace:
                                type: boolean
                              height:
                                type: string
                              maxHeight:
                                type: string
                              maxWidth:
                                type: string
                              minHeight:
                                type: string
                              minWidth:
                                type: string
                              width:
                                type: string
                          type:
                            type: string
                            enum:
                              - inline-spacer
                        required:
                          - instanceKey
                          - type
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                            description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                          padding:
                            anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: 'null'
                          preset:
                            type: string
                            pattern: ^[a-z0-9-]+$
                            description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                          align:
                            type: string
                            enum:
                              - start
                              - center
                              - end
                            description: 'Placement of the line within its slot along the cross axis (default center): horizontal → top/center/bottom, vertical → left/center/right'
                          color:
                            type: string
                            enum:
                              - border1
                              - border4
                              - text1
                              - text4
                            description: Theme color for the line, subtle → bold (default border4)
                          direction:
                            type: string
                            enum:
                              - horizontal
                              - vertical
                            description: Line orientation (default horizontal)
                          style:
                            type: object
                            properties:
                              aspectRatio:
                                type: string
                              fillSpace:
                                type: boolean
                              height:
                                type: string
                              maxHeight:
                                type: string
                              maxWidth:
                                type: string
                              minHeight:
                                type: string
                              minWidth:
                                type: string
                              width:
                                type: string
                          thickness:
                            anyOf:
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 4
                            description: Line thickness in px (default 1)
                          type:
                            type: string
                            enum:
                              - inline-divider
                        required:
                          - instanceKey
                          - type
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                            description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                          padding:
                            anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: array
                                prefixItems:
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                                  - anyOf:
                                      - type: number
                                        enum:
                                          - 0
                                      - type: number
                                        enum:
                                          - 0.5
                                      - type: number
                                        enum:
                                          - 1
                                      - type: number
                                        enum:
                                          - 2
                                      - type: number
                                        enum:
                                          - 3
                                      - type: number
                                        enum:
                                          - 4
                                      - type: number
                                        enum:
                                          - 5
                                      - type: number
                                        enum:
                                          - 6
                                      - type: number
                                        enum:
                                          - 7
                                      - type: number
                                        enum:
                                          - 8
                              - type: 'null'
                          preset:
                            type: string
                            pattern: ^[a-z0-9-]+$
                            description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                          description:
                            type: string
                            description: Lightweight supporting text shown beneath the label, e.g. wire-frame instructions.
                          label:
                            type: string
                            description: Custom heading. Falls back to a type-derived default (e.g. "Placeholder query") when unset.
                          placeholderType:
                            type: string
                            enum:
                              - query
                              - control
                              - filter
                              - text
                            description: The content type this placeholder stands in for. Unset means no type has been picked yet.
                          style:
                            type: object
                            properties:
                              aspectRatio:
                                type: string
                              fillSpace:
                                type: boolean
                              height:
                                type: string
                              maxHeight:
                                type: string
                              maxWidth:
                                type: string
                              minHeight:
                                type: string
                              minWidth:
                                type: string
                              width:
                                type: string
                          type:
                            type: string
                            enum:
                              - placeholder
                        required:
                          - instanceKey
                          - type
                      - type: object
                        properties:
                          instanceKey:
                            type: string
                          type:
                            type: string
                        required:
                          - instanceKey
                          - type
                        additionalProperties: {}
                  - type: object
                    properties:
                      gridPosition:
                        type: object
                        properties:
                          h:
                            type: number
                          w:
                            type: number
                          x:
                            type: number
                          y:
                            type: number
                        required:
                          - h
                          - w
                          - x
                          - y
                    required:
                      - gridPosition
        containerType:
          type: string
          enum:
            - grid
        gridPosition:
          type: object
          properties:
            h:
              type: integer
              minimum: 1
              description: 'Height in grid units (default: 36 for charts)'
            w:
              type: integer
              minimum: 1
              description: 'Width in grid columns on a 24-column grid. Common widths: 24 (full), 12 (half), 8 (third), 6 (quarter). x + w must not exceed 24.'
            x:
              type: integer
              minimum: 0
              description: X position on a 24-column grid (0=left edge, 12=middle). Items side-by-side share the same y with complementary x values.
            y:
              type: integer
              minimum: 0
              description: Y position in grid units (0=top, higher values=lower on page)
          required:
            - h
            - w
            - x
            - y
        metadata:
          type: object
          properties:
            attachedQueryKey:
              type: string
              description: The workbook tab this tile container belongs to. By default this is the `id` of the query content item the container renders. Both use the same numbering as the keys under `queryPresentations`, so a copy that renumbers chart ids must renumber this too; a value naming a different tab makes Explore and Edit in workbook open that other tab. The server removes containers with a matching `attachedQueryKey` when that tab is deleted.
            generatedHeading:
              type: boolean
              description: Marks the auto-injected heading wrapper (title + description row) created for a tile, so it can be labeled generically and treated as managed.
            locked:
              type: boolean
              description: Locks the container's internal arrangement so its children cannot be dragged, reordered, resized, or have new items dropped in. Cascades to all descendants. Does not lock the container's own position/size.
          description: Optional bookkeeping for this container (e.g. `attachedQueryKey` for auto-placed tiles)
        mobileBehavior:
          type: string
          enum:
            - stack
            - wrap
            - keep
            - hide
          description: 'Overrides the automatic mobile layout: "keep" freezes the desktop grid arrangement, "hide" hides the container on mobile ("stack" and "wrap" apply to row stacks)'
        padding:
          anyOf:
            - type: number
              enum:
                - 0
            - type: number
              enum:
                - 0.5
            - type: number
              enum:
                - 1
            - type: number
              enum:
                - 2
            - type: number
              enum:
                - 3
            - type: number
              enum:
                - 4
            - type: number
              enum:
                - 5
            - type: number
              enum:
                - 6
            - type: number
              enum:
                - 7
            - type: number
              enum:
                - 8
            - type: array
              prefixItems:
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
            - type: array
              prefixItems:
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
            - type: array
              prefixItems:
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
            - type: array
              prefixItems:
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
        style:
          type: string
          pattern: ^[a-z0-9-]+$
      required:
        - instanceKey
        - children
        - containerType
      description: Grid container — children are positioned on a grid (each carries a gridPosition).

    PageContainer:
      type: object
      properties:
        description:
          type: string
          description: Optional description for the container, providing additional context or information
        instanceKey:
          type: string
          description: Unique identifier for this container. Used to reference the container when adding, moving, or removing children.
        name:
          type: string
          description: Human-readable name for the container, used for easier reference in logic and design
        breakpoint:
          type: string
          enum:
            - desktop
            - mobile
        container:
          anyOf:
            - $ref: '#/components/schemas/GridContainer'
            - $ref: '#/components/schemas/StackContainer'
            - $ref: '#/components/schemas/ReferenceContainer'
        containerType:
          type: string
          enum:
            - page
        media:
          type: string
          enum:
            - screen
            - print
      required:
        - instanceKey
        - container
        - containerType
      description: Page container — a top-level page wrapping a single grid, stack, or reference container, optionally per breakpoint/media.

    StackContainer:
      type: object
      properties:
        description:
          type: string
          description: Optional description for the container, providing additional context or information
        instanceKey:
          type: string
          description: Unique identifier for this container. Used to reference the container when adding, moving, or removing children.
        name:
          type: string
          description: Human-readable name for the container, used for easier reference in logic and design
        aspectRatio:
          type: string
        fillSpace:
          type: boolean
        height:
          type: string
        maxHeight:
          type: string
        maxWidth:
          type: string
        minHeight:
          type: string
        minWidth:
          type: string
        width:
          type: string
        after:
          anyOf:
            - $ref: '#/components/schemas/StackContainer'
            - $ref: '#/components/schemas/ReferenceContainer'
        align:
          type: string
          enum:
            - flex-start
            - flex-end
            - center
            - stretch
          description: Cross-axis alignment of children (e.g., center, stretch)
        before:
          anyOf:
            - $ref: '#/components/schemas/StackContainer'
            - $ref: '#/components/schemas/ReferenceContainer'
        children:
          type: array
          items:
            anyOf:
              - type: object
                properties:
                  instanceKey:
                    type: string
                    description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                  padding:
                    anyOf:
                      - type: number
                        enum:
                          - 0
                      - type: number
                        enum:
                          - 0.5
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 3
                      - type: number
                        enum:
                          - 4
                      - type: number
                        enum:
                          - 5
                      - type: number
                        enum:
                          - 6
                      - type: number
                        enum:
                          - 7
                      - type: number
                        enum:
                          - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: 'null'
                  preset:
                    type: string
                    pattern: ^[a-z0-9-]+$
                    description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                  as:
                    type: string
                    enum:
                      - chart
                      - result
                      - ai
                      - metadata
                    description: 'Render mode: "chart" for visualization, "result" for data table, "ai" for AI summary, "metadata" for query metadata'
                  detachedContent:
                    type: string
                    description: 'Set when a metadata field is detached: local text rendered instead of the query value'
                  format:
                    type: string
                    enum:
                      - subtitle
                      - description
                      - name
                    description: 'Display format when as is "ai" or "metadata": "name" (metadata only), "subtitle", or "description"'
                  id:
                    type: string
                    pattern: ^[1-9][0-9]*$
                    description: The query presentation ID this content item references
                  name:
                    type: string
                    description: Display name for this query item
                  style:
                    type: object
                    properties:
                      aspectRatio:
                        type: string
                      fillSpace:
                        type: boolean
                      height:
                        type: string
                      maxHeight:
                        type: string
                      maxWidth:
                        type: string
                      minHeight:
                        type: string
                      minWidth:
                        type: string
                      width:
                        type: string
                  type:
                    type: string
                    enum:
                      - query
                required:
                  - instanceKey
                  - id
                  - type
              - type: object
                properties:
                  instanceKey:
                    type: string
                    description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                  padding:
                    anyOf:
                      - type: number
                        enum:
                          - 0
                      - type: number
                        enum:
                          - 0.5
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 3
                      - type: number
                        enum:
                          - 4
                      - type: number
                        enum:
                          - 5
                      - type: number
                        enum:
                          - 6
                      - type: number
                        enum:
                          - 7
                      - type: number
                        enum:
                          - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: 'null'
                  preset:
                    type: string
                    pattern: ^[a-z0-9-]+$
                    description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                  appearance:
                    oneOf:
                      - type: object
                        properties:
                          as:
                            type: string
                            enum:
                              - inline
                        required:
                          - as
                      - type: object
                        properties:
                          as:
                            type: string
                            enum:
                              - tooltip
                        required:
                          - as
                    description: 'Display mode: "inline" renders directly, "tooltip" shows on hover'
                  content:
                    type: string
                    description: The text content to display (supports markdown)
                  name:
                    type: string
                    description: Display name for this text item (e.g., title, subtitle)
                  style:
                    type: object
                    properties:
                      aspectRatio:
                        type: string
                      fillSpace:
                        type: boolean
                      height:
                        type: string
                      maxHeight:
                        type: string
                      maxWidth:
                        type: string
                      minHeight:
                        type: string
                      minWidth:
                        type: string
                      width:
                        type: string
                  textAlign:
                    type: string
                    enum:
                      - start
                      - center
                      - end
                    description: 'Text alignment: "start", "center", or "end"'
                  type:
                    type: string
                    enum:
                      - inline-text
                required:
                  - instanceKey
                  - content
                  - type
              - type: object
                properties:
                  instanceKey:
                    type: string
                    description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                  padding:
                    anyOf:
                      - type: number
                        enum:
                          - 0
                      - type: number
                        enum:
                          - 0.5
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 3
                      - type: number
                        enum:
                          - 4
                      - type: number
                        enum:
                          - 5
                      - type: number
                        enum:
                          - 6
                      - type: number
                        enum:
                          - 7
                      - type: number
                        enum:
                          - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: 'null'
                  preset:
                    type: string
                    pattern: ^[a-z0-9-]+$
                    description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                  appearance:
                    oneOf:
                      - type: object
                        properties:
                          as:
                            type: string
                            enum:
                              - tabs
                          variant:
                            type: string
                            enum:
                              - underline
                              - bordered
                        required:
                          - as
                      - type: object
                        properties:
                          as:
                            type: string
                            enum:
                              - buttons
                          variant:
                            type: string
                            enum:
                              - segment
                              - toggle
                              - pills
                        required:
                          - as
                      - type: object
                        properties:
                          as:
                            type: string
                            enum:
                              - list
                          description:
                            type: boolean
                        required:
                          - as
                      - type: object
                        properties:
                          as:
                            type: string
                            enum:
                              - dropdown
                          description:
                            type: boolean
                        required:
                          - as
                  options:
                    type: array
                    items:
                      type: object
                      properties:
                        disabled:
                          type: boolean
                        id:
                          type: string
                        includeControls:
                          type: boolean
                        label:
                          type: string
                        target:
                          type: string
                        uri:
                          type: string
                        value:
                          type: string
                          description: Page link target. Must exactly equal an existing page container’s instanceKey — not its name, label, or a slug. A value matching no page redirects to the main dashboard view with a "Page not found" message. Ignored when `uri` is set.
                    description: Omit to auto-derive one tab per page, kept in sync as pages are added, removed, renamed, or reordered. Set to curate, reorder, or add custom-URL tabs; each option’s `value` must then match a page instanceKey.
                  style:
                    type: object
                    properties:
                      aspectRatio:
                        type: string
                      fillSpace:
                        type: boolean
                      height:
                        type: string
                      maxHeight:
                        type: string
                      maxWidth:
                        type: string
                      minHeight:
                        type: string
                      minWidth:
                        type: string
                      width:
                        type: string
                  type:
                    type: string
                    enum:
                      - inline-page-switcher
                required:
                  - instanceKey
                  - type
              - type: object
                properties:
                  instanceKey:
                    type: string
                    description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                  padding:
                    anyOf:
                      - type: number
                        enum:
                          - 0
                      - type: number
                        enum:
                          - 0.5
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 3
                      - type: number
                        enum:
                          - 4
                      - type: number
                        enum:
                          - 5
                      - type: number
                        enum:
                          - 6
                      - type: number
                        enum:
                          - 7
                      - type: number
                        enum:
                          - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: 'null'
                  preset:
                    type: string
                    pattern: ^[a-z0-9-]+$
                    description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                  id:
                    type: string
                    format: uuid
                    description: UUID of the rich text content block
                  style:
                    type: object
                    properties:
                      aspectRatio:
                        type: string
                      fillSpace:
                        type: boolean
                      height:
                        type: string
                      maxHeight:
                        type: string
                      maxWidth:
                        type: string
                      minHeight:
                        type: string
                      minWidth:
                        type: string
                      width:
                        type: string
                  type:
                    type: string
                    enum:
                      - text
                required:
                  - instanceKey
                  - id
                  - type
              - type: object
                properties:
                  instanceKey:
                    type: string
                    description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                  padding:
                    anyOf:
                      - type: number
                        enum:
                          - 0
                      - type: number
                        enum:
                          - 0.5
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 3
                      - type: number
                        enum:
                          - 4
                      - type: number
                        enum:
                          - 5
                      - type: number
                        enum:
                          - 6
                      - type: number
                        enum:
                          - 7
                      - type: number
                        enum:
                          - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: 'null'
                  preset:
                    type: string
                    pattern: ^[a-z0-9-]+$
                    description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                  id:
                    type: string
                  type:
                    type: string
                    enum:
                      - control
                  style:
                    type: object
                    properties:
                      aspectRatio:
                        type: string
                      fillSpace:
                        type: boolean
                      height:
                        type: string
                      maxHeight:
                        type: string
                      maxWidth:
                        type: string
                      minHeight:
                        type: string
                      minWidth:
                        type: string
                      width:
                        type: string
                required:
                  - instanceKey
                  - id
                  - type
              - type: object
                properties:
                  instanceKey:
                    type: string
                    description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                  padding:
                    anyOf:
                      - type: number
                        enum:
                          - 0
                      - type: number
                        enum:
                          - 0.5
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 3
                      - type: number
                        enum:
                          - 4
                      - type: number
                        enum:
                          - 5
                      - type: number
                        enum:
                          - 6
                      - type: number
                        enum:
                          - 7
                      - type: number
                        enum:
                          - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: 'null'
                  preset:
                    type: string
                    pattern: ^[a-z0-9-]+$
                    description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                  id:
                    type: string
                  type:
                    type: string
                    enum:
                      - filter
                  appearance:
                    type: object
                    properties:
                      control:
                        type: string
                        enum:
                          - buttonToggle
                          - dropdown
                      display:
                        type: string
                        enum:
                          - inline
                          - popover
                      options:
                        type: array
                        items:
                          type: object
                          properties:
                            label:
                              type: string
                            value:
                              type: string
                          required:
                            - value
                      value:
                        type: array
                        items:
                          type: string
                  style:
                    type: object
                    properties:
                      aspectRatio:
                        type: string
                      fillSpace:
                        type: boolean
                      height:
                        type: string
                      maxHeight:
                        type: string
                      maxWidth:
                        type: string
                      minHeight:
                        type: string
                      minWidth:
                        type: string
                      width:
                        type: string
                required:
                  - instanceKey
                  - id
                  - type
              - type: object
                properties:
                  instanceKey:
                    type: string
                    description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                  padding:
                    anyOf:
                      - type: number
                        enum:
                          - 0
                      - type: number
                        enum:
                          - 0.5
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 3
                      - type: number
                        enum:
                          - 4
                      - type: number
                        enum:
                          - 5
                      - type: number
                        enum:
                          - 6
                      - type: number
                        enum:
                          - 7
                      - type: number
                        enum:
                          - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: 'null'
                  preset:
                    type: string
                    pattern: ^[a-z0-9-]+$
                    description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                  size:
                    type: number
                    minimum: 0
                    maximum: 2000
                    description: Fixed size in px along the parent stack axis (default 16). In grids the size comes from gridPosition; ignored when style.fillSpace is true.
                  style:
                    type: object
                    properties:
                      aspectRatio:
                        type: string
                      fillSpace:
                        type: boolean
                      height:
                        type: string
                      maxHeight:
                        type: string
                      maxWidth:
                        type: string
                      minHeight:
                        type: string
                      minWidth:
                        type: string
                      width:
                        type: string
                  type:
                    type: string
                    enum:
                      - inline-spacer
                required:
                  - instanceKey
                  - type
              - type: object
                properties:
                  instanceKey:
                    type: string
                    description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                  padding:
                    anyOf:
                      - type: number
                        enum:
                          - 0
                      - type: number
                        enum:
                          - 0.5
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 3
                      - type: number
                        enum:
                          - 4
                      - type: number
                        enum:
                          - 5
                      - type: number
                        enum:
                          - 6
                      - type: number
                        enum:
                          - 7
                      - type: number
                        enum:
                          - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: 'null'
                  preset:
                    type: string
                    pattern: ^[a-z0-9-]+$
                    description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                  align:
                    type: string
                    enum:
                      - start
                      - center
                      - end
                    description: 'Placement of the line within its slot along the cross axis (default center): horizontal → top/center/bottom, vertical → left/center/right'
                  color:
                    type: string
                    enum:
                      - border1
                      - border4
                      - text1
                      - text4
                    description: Theme color for the line, subtle → bold (default border4)
                  direction:
                    type: string
                    enum:
                      - horizontal
                      - vertical
                    description: Line orientation (default horizontal)
                  style:
                    type: object
                    properties:
                      aspectRatio:
                        type: string
                      fillSpace:
                        type: boolean
                      height:
                        type: string
                      maxHeight:
                        type: string
                      maxWidth:
                        type: string
                      minHeight:
                        type: string
                      minWidth:
                        type: string
                      width:
                        type: string
                  thickness:
                    anyOf:
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 4
                    description: Line thickness in px (default 1)
                  type:
                    type: string
                    enum:
                      - inline-divider
                required:
                  - instanceKey
                  - type
              - type: object
                properties:
                  instanceKey:
                    type: string
                    description: Unique identifier for this specific placement of the content item. Use this key (not the query id) when repositioning or removing items.
                  padding:
                    anyOf:
                      - type: number
                        enum:
                          - 0
                      - type: number
                        enum:
                          - 0.5
                      - type: number
                        enum:
                          - 1
                      - type: number
                        enum:
                          - 2
                      - type: number
                        enum:
                          - 3
                      - type: number
                        enum:
                          - 4
                      - type: number
                        enum:
                          - 5
                      - type: number
                        enum:
                          - 6
                      - type: number
                        enum:
                          - 7
                      - type: number
                        enum:
                          - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: array
                        prefixItems:
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                          - anyOf:
                              - type: number
                                enum:
                                  - 0
                              - type: number
                                enum:
                                  - 0.5
                              - type: number
                                enum:
                                  - 1
                              - type: number
                                enum:
                                  - 2
                              - type: number
                                enum:
                                  - 3
                              - type: number
                                enum:
                                  - 4
                              - type: number
                                enum:
                                  - 5
                              - type: number
                                enum:
                                  - 6
                              - type: number
                                enum:
                                  - 7
                              - type: number
                                enum:
                                  - 8
                      - type: 'null'
                  preset:
                    type: string
                    pattern: ^[a-z0-9-]+$
                    description: Visual preset for the content item (e.g., "tile-align" for outer spacing to align with tile containers)
                  description:
                    type: string
                    description: Lightweight supporting text shown beneath the label, e.g. wire-frame instructions.
                  label:
                    type: string
                    description: Custom heading. Falls back to a type-derived default (e.g. "Placeholder query") when unset.
                  placeholderType:
                    type: string
                    enum:
                      - query
                      - control
                      - filter
                      - text
                    description: The content type this placeholder stands in for. Unset means no type has been picked yet.
                  style:
                    type: object
                    properties:
                      aspectRatio:
                        type: string
                      fillSpace:
                        type: boolean
                      height:
                        type: string
                      maxHeight:
                        type: string
                      maxWidth:
                        type: string
                      minHeight:
                        type: string
                      minWidth:
                        type: string
                      width:
                        type: string
                  type:
                    type: string
                    enum:
                      - placeholder
                required:
                  - instanceKey
                  - type
              - type: object
                properties:
                  instanceKey:
                    type: string
                  type:
                    type: string
                required:
                  - instanceKey
                  - type
                additionalProperties: {}
              - $ref: '#/components/schemas/ReferenceContainer'
              - $ref: '#/components/schemas/GridContainer'
              - $ref: '#/components/schemas/StackContainer'
        containerType:
          type: string
          enum:
            - stack
          description: Stack containers lay out children sequentially in a direction (column or row)
        direction:
          type: string
          enum:
            - column
            - row
          description: 'Layout direction: "row" lays out horizontally, "column" stacks vertically (default if not specified)'
        gap:
          anyOf:
            - type: number
              enum:
                - 0
            - type: number
              enum:
                - 0.5
            - type: number
              enum:
                - 1
            - type: number
              enum:
                - 2
            - type: number
              enum:
                - 3
            - type: number
              enum:
                - 4
            - type: number
              enum:
                - 5
            - type: number
              enum:
                - 6
            - type: number
              enum:
                - 7
            - type: number
              enum:
                - 8
            - type: number
              enum:
                - 9
            - type: number
              enum:
                - 10
            - type: number
              enum:
                - 11
            - type: number
              enum:
                - 12
            - type: number
              enum:
                - 13
            - type: number
              enum:
                - 14
            - type: number
              enum:
                - 15
            - type: number
              enum:
                - 16
          description: Space between children (CSS size value)
        justify:
          type: string
          enum:
            - flex-start
            - flex-end
            - center
            - space-between
          description: Main-axis alignment of children (e.g., start, center, end)
        metadata:
          type: object
          properties:
            attachedQueryKey:
              type: string
              description: The workbook tab this tile container belongs to. By default this is the `id` of the query content item the container renders. Both use the same numbering as the keys under `queryPresentations`, so a copy that renumbers chart ids must renumber this too; a value naming a different tab makes Explore and Edit in workbook open that other tab. The server removes containers with a matching `attachedQueryKey` when that tab is deleted.
            generatedHeading:
              type: boolean
              description: Marks the auto-injected heading wrapper (title + description row) created for a tile, so it can be labeled generically and treated as managed.
            locked:
              type: boolean
              description: Locks the container's internal arrangement so its children cannot be dragged, reordered, resized, or have new items dropped in. Cascades to all descendants. Does not lock the container's own position/size.
          description: Optional bookkeeping for this container (e.g. `attachedQueryKey` for auto-placed tiles)
        mobileBehavior:
          type: string
          enum:
            - stack
            - wrap
            - keep
            - hide
          description: 'Overrides the automatic mobile layout: "stack" flips a row to a column, "wrap" keeps the row and wraps items, "keep" freezes the desktop arrangement, "hide" hides the container on mobile'
        padding:
          anyOf:
            - type: number
              enum:
                - 0
            - type: number
              enum:
                - 0.5
            - type: number
              enum:
                - 1
            - type: number
              enum:
                - 2
            - type: number
              enum:
                - 3
            - type: number
              enum:
                - 4
            - type: number
              enum:
                - 5
            - type: number
              enum:
                - 6
            - type: number
              enum:
                - 7
            - type: number
              enum:
                - 8
            - type: array
              prefixItems:
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
            - type: array
              prefixItems:
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
            - type: array
              prefixItems:
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
            - type: array
              prefixItems:
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
                - anyOf:
                    - type: number
                      enum:
                        - 0
                    - type: number
                      enum:
                        - 0.5
                    - type: number
                      enum:
                        - 1
                    - type: number
                      enum:
                        - 2
                    - type: number
                      enum:
                        - 3
                    - type: number
                      enum:
                        - 4
                    - type: number
                      enum:
                        - 5
                    - type: number
                      enum:
                        - 6
                    - type: number
                      enum:
                        - 7
                    - type: number
                      enum:
                        - 8
        style:
          type: string
          pattern: ^[a-z0-9-]+$
        wrap:
          type: string
          enum:
            - nowrap
            - wrap
          description: Whether items in a row stack wrap to new lines (defaults to nowrap if not specified). Ignored on column stacks.
      required:
        - instanceKey
        - children
        - containerType
      description: Stack container — an ordered list of nested children (content, grid, stack, or reference).

    DashboardExportLayoutItem:
      type: object
      properties:
        h:
          type: number
          description: Height in grid units
        i:
          type: string
          description: Item identifier
        moved:
          type: boolean
          description: Whether item was moved
        static:
          type: boolean
          description: Whether item is static
        w:
          type: number
          description: Width in grid units
        x:
          type: number
          description: X position in grid
        y:
          type: number
          description: Y position in grid
      required:
        - h
        - i
        - w
        - x
        - y

    TextTile:
      type: object
      properties:
        content:
          type: string
          description: Text content
        miniUuid:
          type: string
          description: Tile identifier
      required:
        - content
        - miniUuid

    DashboardMetadataVersion:
      type: number
      enum:
        - 2
      description: Dashboard metadata version (required when metadata is provided)

    DocumentPresentation:
      type: object
      properties:
        filters:
          type: object
          properties:
            collapsible:
              type: boolean
              description: Whether filters are collapsible
            defaultExpanded:
              type: boolean
              description: Whether filters are expanded by default
            fixedControlBar:
              type: boolean
              default: false
              description: Whether the control bar is fixed to the top of the dashboard while scrolling
          default:
            collapsible: true
            defaultExpanded: false
            fixedControlBar: false
          required:
            - collapsible
            - defaultExpanded
          description: Filter presentation settings
      description: Presentation settings

    DocumentIdentifier:
      type: string
      minLength: 2
      maxLength: 48
      description: Optional document identifier. If omitted, an identifier is auto-generated. Must be unique within the organization.

    ReferenceContainer:
      type: object
      properties:
        containerType:
          type: string
          enum:
            - reference
        instanceKey:
          type: string
        referenceKey:
          type: string
      required:
        - containerType
        - instanceKey
        - referenceKey
      description: Reference container — points at another container in the collection by its instanceKey.

    ApiError400:
      type: object
      properties:
        detail:
          type: string
          description: Human-readable error message describing what went wrong.
          # example: "Bad Request: prompt: Required"
        status:
          type: integer
          description: HTTP status code of the error.
          example: 400
      required:
        - detail
        - status

    ApiError401:
      type: object
      properties:
        detail:
          type: string
          description: Human-readable error message describing what went wrong.
          example: "Unauthorized: Missing or invalid API key"
        status:
          type: integer
          description: HTTP status code of the error.
          example: 401
      required:
        - detail
        - status

    ApiError403:
      type: object
      properties:
        detail:
          type: string
          description: Human-readable error message describing what went wrong.
          # example: "Forbidden: AI query generation is not enabled for this organization"
        status:
          type: integer
          description: HTTP status code of the error.
          example: 403
      required:
        - detail
        - status

    ApiError404:
      type: object
      properties:
        detail:
          type: string
          description: Human-readable error message describing what went wrong.
          # example: "Model 770e8400-e29b-41d4-a716-446655440002 not found"
        status:
          type: integer
          description: HTTP status code of the error.
          example: 404
      required:
        - detail
        - status

    QueryTimeoutResponse:
      type: object
      properties:
        detail:
          type: string
          description: Error message indicating the query timed out.
          example: Query timed out
        remaining_job_ids:
          type: array
          items:
            type: string
          description: Job IDs for queries that have not yet completed. Use /api/v1/query/wait to poll for results.
        timed_out:
          type: boolean
          enum:
            - true
          description: Always true for timeout responses.
          example: true
      required:
        - detail
        - timed_out

    ApiError410:
      description: |
        Endpoint has been removed.

        This response includes the following headers:

        - `Deprecation` - `true`, indicating the endpoint is deprecated
        - `Sunset` - The date the endpoint was removed (`2026-07-31`)
        - `Link` - Link to the replacement API. For example, `</api/documents-v2/create-document>; rel="successor-version"`
      headers:
        Deprecation:
          description: Indicates that this endpoint is deprecated
          schema:
            type: string
            example: "true"
        Sunset:
          description: The date this endpoint will be removed (RFC 9110)
          schema:
            type: string
            format: date
            example: "2026-07-31"
        Link:
          description: Link to the replacement API (RFC 8288)
          schema:
            type: string
            example: |
              </api/documents-v2/create-document>; rel="successor-version"

    ApiError409:
      type: object
      properties:
        detail:
          type: string
          description: Human-readable error message describing what went wrong.
          # example: "An active job already exists for this conversation"
        status:
          type: integer
          description: HTTP status code of the error.
          example: 409
      required:
        - detail
        - status

    ApiError429:
      type: object
      properties:
        detail:
          type: string
          description: Human-readable error message describing what went wrong.
          # example: User has reached the maximum of 100 routines
        status:
          type: integer
          description: HTTP status code of the error.
          example: 429
      required:
        - detail
        - status

    ScheduleContent:
      type: string
      enum:
        - dashboard
        - tile
        - workbook tab
      description: 'Content type: dashboard, tile, or workbook tab'
      example: dashboard

    ScheduleCron:
      type: string
      description: AWS EventBridge cron expression (minute hour day-of-month month day-of-week year)
      example: 0 9 ? * MON *

    ScheduleAlertObject:
      type: object
      description: Alert configuration. Only present for alert-type schedules.
      properties:
        conditionQueryName:
          type: string
          nullable: true
          description: Name of the query used for alert condition
        conditionType:
          nullable: true
          $ref: '#/components/schemas/ScheduleConditionType'
      required:
        - conditionQueryName
        - conditionType

    ScheduleConditionQueryMapKey:
      type: string
      description: |
        **Required if `conditionType` is provided.** The ID of the query to monitor for triggering an alert. Must reference a valid query in the document. For a workbook delivery, this must be a tab of the published workbook.

        In responses, null for standard schedules.
      example: Jmn2r3KV
    
    ScheduleConditionType:
      type: string
      enum:
        - RESULTS_CHANGED
        - RESULTS_UNCHANGED
        - RESULTS_PRESENT
        - RESULTS_MISSING
      description: |
        **Required if `conditionQueryMapKey` is provided.** Defines the type of condition to use for alerts.
      example: RESULTS_PRESENT

    ScheduleDestinationType:
      type: string
      enum:
        - email
        - slack
        - webhook
        - sftp
        - s3
        - google_sheets
      description: 'Delivery destination type: email, slack, webhook, sftp, s3, google_sheets'
      example: email

    ScheduleSystemDisabledAt:
      type: string
      format: date-time
      description: Timestamp when the system disabled the schedule

    ScheduleSystemDisabledReason:
      type: string
      nullable: true
      enum:
        - contentRemoved
        - credentialMissing
        - destinationDisabled
        - missingQuery
        - noAccess
        - orphanedFilterConfigKeys
        - ownerRevoked
      description: |
        Reason for the system disabling the schdule.

    ScheduleDisabledAt:
      type: string
      nullable: true
      format: date-time
      description: Timestamp when the schedule was paused. Null if active.

    ScheduleEnableConditionalFormatting:
      type: boolean
      default: true
      description: Compatible with `xlsx` format only. When `true`, conditional formatting rules from the visualization are included in the Excel output.
      example: true

    ScheduleEnableFormatting:
      type: boolean
      default: false
      description: |
        **Compatible with `csv`, `xlsx`, and `json` formats.** If `true`, number and date formatting will be preserved in the task's output.

        If `true` for `json` format, `queryIdentifierMapKey` is required.
      example: false

    ExpandAiSummaries:
      type: boolean
      example: false
      description: |
        **Compatible with `pdf` and `png` formats.** If `true`, AI summary tiles grow to show their full text, splitting across pages when `paperFormat` isn't `fit_page`.

        With `paperFormat: fit_page`, the page grows instead, and the operation fails if the page would be taller than 60,000px.

    ScheduleExpandTablesToShowAllRows:
      type: boolean
      example: false
      description: |
        **Compatible with `pdf` and `png` formats.** If `true`, up to 1,000 rows in table visualizations will be included in the delivery.

        With `paperFormat: fit_page`, the page grows to fit the rows, and the delivery fails if the page would be taller than 60,000px.

    ScheduleFilterConfig:
      type: object
      description: |
        Dashboard deliveries only. An object specifying the filter conditions to apply to the task. The filter key specified must already exist in the dashboard.

        Use the [Get dashboard filters and controls endpoint](/api/dashboard-filters/get-dashboard-filters-and-controls) to retrieve filter configuration objects.

        **Note**: Filter keys are case-sensitive and must match exactly.
      example:
        status:
          - active
          - pending

    ScheduleFormat:
      type: string
      enum:
        - link_only
        - pdf
        - png
        - csv
        - xlsx
        - json
      description: The output format of the task.
      example: pdf

    ScheduleHiddenFields:
      type: boolean
      default: false
      description: |
        **Compatible with `csv` and `xlsx` formats.** If `true`, fields marked as `hidden` won't be displayed in the task's output.
      example: false

    ScheduleHideTitle:
      type: boolean
      default: false
      description: |
        **Compatible with `pdf` and `png` formats.** If `true`, the content's title will be hidden in the task's output.
      example: false

    ScheduleId:
      type: string
      format: uuid
      description: Unique identifier for the schedule

    ScheduleMaxRowLimit:
      type: number
      minimum: 1
      description: |
        **Compatible with `csv`, `json`, and `xlsx` formats.** Used with `overrideRowLimit`. Specifies the maximum number of rows.
      example: 1000

    ScheduleName:
      type: string
      description: Name of the schedule
      example: Weekly Sales Report

    ScheduleOverrideRowLimit:
      type: boolean
      default: false
      description: |
        **Compatible with `csv`, `json`, and `xlsx` formats.** If `true`, the default row limit will be overridden.

        If `true` for `json` and `xlsx` formats, a `queryIdentifierMapKey` is required.
      example: false

    SchedulePaperFormat:
      type: string
      enum:
        - a3
        - a4
        - fit_page
        - legal
        - letter
        - tabloid
      description: |
        **Compatible with `pdf` format.** Defines the paper format (size) of the resulting PDF.
      example: letter

    SchedulePaperOrientation:
      type: string
      enum:
        - portrait
        - landscape
      description: |
        **Compatible with `pdf` format.** Defines the paper orientation of the resulting PDF.
      example: landscape

    ScheduleContainerPages:
      type: array
      items:
        type: string
      minItems: 1
      description: |
        Dashboard deliveries only. **Compatible with `pdf`, `png`, `csv`, and `xlsx` formats, on dashboards using the advanced (multi-page) layout.** The `instanceKey`s of the pages to render:

        - `pdf` renders the listed pages in storage order.
        - `png` accepts exactly one page.
        - `csv` and `xlsx` keep only the tiles on the selected pages.

        Cannot be combined with `queryIdentifierMapKey`. Defaults to the dashboard's first page for `pdf` and `png`, and to every tile for `csv` and `xlsx`.
      example:
        - "01KXH5TG58BB5JBJHGTPC62RZA"

    ScheduleQueryIdentifierMapKey:
      type: string
      description: |
        **Required for single tile tasks and for workbook deliveries (`entity: workbook`)**. The ID of the query to include in a single tile task, or the ID of the workbook tab to deliver. Must reference a valid query in the document.

        **Required for:**
        - `format: json` with `enableFormatting: true`
        - `format: xlsx` with `overrideRowLimit: true`
        - `entity: workbook`
      example: Jmn2r3KV

    ScheduleKillJobsOnFailure:
      type: boolean
      default: false
      description: If `true`, stops the entire job if any queries included in the task fail.
      example: false

    ScheduleLastCompletedAt:
      type: string
      format: date-time
      description: Timestamp of last completed delivery
    
    ScheduleLastStatus:
      type: string
      enum:
        - COMPLETE
        - ERROR
        - ERROR_DELIVERED
        - KILLED
        - CONDITION_UNMET
      description: Status of last delivery

    ScheduleOwner:
      type: object
      properties:
        name:
          type: string
          description: Schedule owner name
      required:
        - name
    
    ScheduleOwnerId:
      type: string
      format: uuid
      description: |
        The user ID of the schedule owner, as returned by the [List users](/api/users/list-users) endpoint.

        In the rare case the owner has no membership in the organization at all (for example, an embed user removed through a hard delete), this falls back to the owner's raw user ID instead.

    ScheduleRebuildExtracts:
      type: boolean
      default: false
      description: If `true`, rebuild extracts before running the schedule. Cannot be combined with `useCache`.
      example: false

    ScheduleShowContentLink:
      type: boolean
      default: true
      description: |
        **Compatible with all formats except `link_only`.** If `true`, a link to the content will be shown in the task's output.
      example: true
    
    ScheduleShowFilters:
      type: boolean
      default: true
      description: |
        **Compatible with all formats except `link_only` and `csv`.** If `true`, filters will be shown in the task's output.
      example: true

    ScheduleSingleColumnLayout:
      type: boolean
      description: |
        Dashboard deliveries only. **Compatible with `pdf` and `png` formats.** If `true`, dashboard tiles will be arranged into a single vertical column.
      example: false
    
    ScheduleUseCache:
      type: boolean
      default: false
      description: If `true`, allow scheduled queries to use cached results instead of always running fresh queries. An alert always runs its condition check fresh. Only the delivery sent once the condition is met uses cached results. Cannot be combined with `rebuildExtracts`.
      example: false

    ScheduleSlackGroupDm:
      type: boolean
      default: false
      description: |
        **Slack users only.** If `true`, delivers each run as a single group direct message to everyone listed, instead of a separate direct message to each recipient. Requires the Slack app to have the `mpim:write` scope. Allows at most eight users.
      example: false

    ScheduleEmailFanOut:
      anyOf:
        - type: boolean
        - type: string
      default: false
      description: |
        **Applicable to email destinations.** If `true`, send individual emails to each recipient.

        If your organization's [delivery personalization setting](/administration/content-permissions#delivery-personalization) is set to **Never**, `fanOut` is always persisted as `false` regardless of the submitted value. If set to **Always**, personalization is forced on for all deliveries. See [Personalizing email deliveries](/share/deliveries/email/personalization) for more information.

        **Note**: Personalized deliveries aren't supported for connections that use OAuth sign-in. In this case, the delivery will fail.

    ScheduleRecipientsEmail:
      type: object
      description: Response for schedules with email destinations.
      properties:
        type:
          type: string
          enum: [email]
          description: The destination type.
        recipients:
          type: array
          items:
            type: object
            properties:
              email:
                type: string
                format: email
                description: The recipient's email address.
              emailOnly:
                type: boolean
                description: Indicates whether the recipient is an email-only user.
              id:
                type: string
                format: uuid
                description: The recipient's unique identifier.
              name:
                type: string
                description: The recipient's name.
        userGroupRecipients:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                description: The user group's ID.
                example: "X9kQ2mPz"
              name:
                type: string
                description: The user group's name.
              recipients:
                type: array
                items:
                  type: object
                  properties:
                    email:
                      type: string
                      format: email
                      description: The recipient's email address.
                    id:
                      type: string
                      format: uuid
                      description: The recipient's unique identifier.
                    name:
                      type: string
                      description: The recipient's name.
                description: The email recipients who belong to the user group.
          description: Recipients added as members of a user group, grouped by user group.

    ScheduleSftpAddress:
      type: string
      description: |
        **Required if `destinationType: sftp`.** SFTP server address. Must be a public host: IP addresses in private, loopback, link-local or reserved ranges, and `.internal`, `.local` or `.localhost` hosts, are rejected.

    ScheduleSftpPort:
      type: string
      description: |
        **Required if `destinationType: sftp`.** SFTP port.

    ScheduleSftpUsername:
      type: string
      description: |
        **Required if `destinationType: sftp`.** SFTP username.

    ScheduleRecipientsSftp:
      type: object
      description: Response for schedules with SFTP destinations.
      properties:
        type:
          type: string
          enum: [sftp]
          description: The destination type.
        address:
          type: string
          description: The SFTP server address.
        port:
          type: integer
          description: The SFTP server port.
        username:
          $ref: '#/components/schemas/ScheduleSftpUsername'
          description: The SFTP username.

    ScheduleSlackRecipientType:
      type: string
      enum: [channel, users]
      description: |
        **Required if `destinationType: slack`.** Specifies whether recipients are a channel or individual users.

        - `channel`: Deliver to a Slack channel. If the channel is private, you'll also need to invite the Omni Slackbot to the channel.
        - `users`: Deliver to individual Slack users

    ScheduleRecipientsSlack:
      type: object
      description: Response for schedules with Slack destinations.
      properties:
        type:
          type: string
          enum: [slack]
          description: The destination type.
        recipients:
          type: array
          items:
            type: object
            properties:
              recipientType:
                $ref: '#/components/schemas/ScheduleSlackRecipientType'
                description: |
                  The type of Slack recipient:
                  - `channel` - A Slack channel
                  - `users` - A Slack user
              slackId:
                type: string
                description: The Slack channel or user ID.

    ScheduleTimezone:
      type: string
      description: IANA timezone identifier
      example: America/New_York

    ScheduleWebhookUrl:
      type: string
      format: uri
      description: |
        A webhook URL. Must be a public HTTPS URL: IP addresses in private, loopback, link-local or reserved ranges, and `.internal`, `.local` or `.localhost` hosts, are rejected.

    ScheduleRecipientsWebhook:
      type: object
      description: Response for schedules with webhook destinations.
      properties:
        type:
          type: string
          enum: [webhook]
          description: The destination type.
        url:
          $ref: '#/components/schemas/ScheduleWebhookUrl'

    ScheduleS3BucketName:
      type: string
      description: |
        **Required if `destinationType: s3`.** The name of the target S3 bucket. Must be 3-63 characters, lowercase letters, numbers, hyphens, and periods only.
      example: "my-reports-bucket"

    ScheduleS3Region:
      type: string
      description: |
        **Required if `destinationType: s3`.** The AWS region of the S3 bucket (e.g., `us-east-1`).
      example: "us-east-1"

    ScheduleS3RoleArn:
      type: string
      description: |
        **Required if `destinationType: s3`.** The ARN of the IAM role in the customer's AWS account that grants Omni write access to the S3 bucket.
      example: "arn:aws:iam::123456789012:role/OmniS3DeliveryRole"

    ScheduleS3KeyPrefix:
      type: string
      description: |
        **Applicable to S3 destinations.** Folder path prefix for the uploaded file (e.g., `reports/daily/`). Must not contain path traversal sequences.
      example: "reports/daily/"

    ScheduleS3Filename:
      type: string
      description: |
        **Applicable to S3 destinations.** Filename template (without extension) using Mustache syntax. Supported variables:

        - `{{currentDate}}`
        - `{{currentTime}}`
        - `{{currentYear}}`
        - `{{currentMonth}}`
        - `{{currentMonthName}}`
        - `{{currentDay}}`
        - `{{yesterdayDate}}`
        - `{{tomorrowDate}}`
        - `{{previousMonth}}`
        - `{{previousMonthName}}`
        - `{{previousMonthYear}}`
        - `{{timeZone}}`
        - `{{entityName}}`
        - `{{format}}`
        - `{{scheduledTaskName}}`
      example: "{{scheduledTaskName}}_{{currentDate}}"

    ScheduleRecipientsS3:
      type: object
      description: Response for schedules with Amazon S3 destinations.
      properties:
        type:
          type: string
          enum: [s3]
          description: The destination type.
        bucketName:
          $ref: '#/components/schemas/ScheduleS3BucketName'
          description: The name of the S3 bucket.
        region:
          $ref: '#/components/schemas/ScheduleS3Region'
          description: The AWS region of the S3 bucket.
        roleArn:
          $ref: '#/components/schemas/ScheduleS3RoleArn'
          description: The ARN of the customer's IAM role.
        externalId:
          type: string
          description: The external ID for IAM trust policy configuration.
        keyPrefix:
          $ref: '#/components/schemas/ScheduleS3KeyPrefix'
          description: The folder path prefix for uploaded files.
        filename:
          $ref: '#/components/schemas/ScheduleS3Filename'
          description: The filename template using Mustache syntax.

    ScheduleTimzeone:
      type: string
      description: IANA timezone identifier used to evaluate the schedule.
      example: America/New_York

    DashboardFiltersResponse:
      type: object
      properties:
        identifier:
          type: string
          description: The dashboard identifier
        filters:
          type: object
          description: Map of filter ID to filter configuration
          additionalProperties:
            $ref: '#/components/schemas/DashboardFilter'
        controls:
          type: array
          description: Array of control configurations
          items:
            $ref: '#/components/schemas/DashboardControl'
        filterOrder:
          type: array
          description: Ordered list of filter and control IDs for display
          items:
            type: string

    DashboardFilter:
      type: object
      description: |
        Filter configuration.
      properties:
        type:
          type: string
          description: |
            The filter data type.

            - `string`: Text-based filters (equals, contains, etc.)
            - `number`: Numeric filters (equals, between, etc.)
            - `date`: Date range filters
            - `boolean`: True/false filters
            - `null`: Null check filters
            - `by_query`: Query-based dynamic filters
            - `user_attribute`: Filters based on user attributes
            - `composite`: Combined filters
          enum:
            - string
            - number
            - date
            - boolean
            - "null"
            - by_query
            - user_attribute
            - composite
        kind:
          type: string
          description: The filter operation kind (e.g., `EQUALS`, `WITHIN_RANGE`)
        values:
          type: array
          description: Default values for string/number filters
          items:
            type: string
        left_side:
          type: string
          description: Start value for date range filters
        right_side:
          type: string
          description: End value for date range filters
        label:
          type: string
          description: Display label
        description:
          type: string
          description: Help text description
        required:
          type: boolean
          description: Whether the filter is required
        requiredScope:
          type: string
          enum:
            - dashboard
            - tiles
          description: |
            Controls what an unset required filter blocks. Only meaningful when `required` is `true`.

            - `dashboard` - An unset filter blocks the entire dashboard
            - `tiles` - An unset filter only blocks the tiles the filter is mapped to
        hidden:
          type: boolean
          description: Whether the filter is hidden

    DashboardControl:
      type: object
      description: |
        Control configuration.
      properties:
        id:
          type: string
          description: Control identifier
        type:
          type: string
          description: |
            The control type:

            - `FIELD_SELECTION`: Single field selector dropdown
            - `MULTI_FIELD_SELECTION`: Parent control for grouping child controls
            - `FIELD_PICKER`: Multi-select field picker
            - `PERIOD_OVER_PERIOD`: Time comparison control
          enum:
            - FIELD_SELECTION
            - MULTI_FIELD_SELECTION
            - FIELD_PICKER
            - PERIOD_OVER_PERIOD
        kind:
          type: string
          description: |
            The control kind, ex: `FIELD`
        label:
          type: string
          description: Display label
        fieldOrder:
          type: string
          enum:
            - query
            - control
          description: |
            Controls how fields are ordered when selected from the control. When `control`, fields are added in the order they are selected from the control. When `query`, fields already in the query retain their position and the control only adds or removes fields. See the [Dashboard control documentation](/visualize-present/dashboards/controls#multi-field-reordering) for more information.
        description:
          type: string
          description: Help text description
        hidden:
          type: boolean
          description: Whether the control is hidden
        field:
          type: string
          description: |
            **Applicable to `FIELD_SELECTION` controls.** The currently selected field.
        options:
          type: array
          description: Available options for field selection
          items:
            type: object
            properties:
              label:
                type: string
                description: Option display label
              value:
                type: string
                description: Option value

    DashboardFiltersUpdateRequest:
      type: object
      description: Request body for updating dashboard filters and controls. At least one of `filters`, `controls`, or `filterOrder` must be provided with at least one entry.
      properties:
        clearExistingDraft:
          type: boolean
          default: false
          description: When `true`, discards any existing draft before applying updates
        filters:
          type: object
          description: Map of filter ID to partial update
          additionalProperties:
            $ref: '#/components/schemas/DashboardFilterUpdate'
        controls:
          type: object
          description: Map of control ID to partial update
          additionalProperties:
            $ref: '#/components/schemas/DashboardControlUpdate'
        filterOrder:
          type: array
          description: New display order (filter and control IDs)
          items:
            type: string

    DashboardFilterUpdate:
      type: object
      description: Partial update for a dashboard filter
      properties:
        values:
          type: array
          description: New default values for string/number filters
          items:
            type: string
        left_side:
          type: string
          description: New start value for date filters
        right_side:
          type: string
          description: New end value for date filters
        label:
          type: string
          description: Display label
        description:
          type: string
          description: Help text description
        required:
          type: boolean
          description: Whether the filter is required
        requiredScope:
          type: string
          enum:
            - dashboard
            - tiles
          description: |
            Controls what an unset required filter blocks. Only meaningful when `required` is `true`.

            - `dashboard` - An unset filter blocks the entire dashboard
            - `tiles` - An unset filter only blocks the tiles the filter is mapped to
        hidden:
          type: boolean
          description: Whether to hide the filter

    DashboardIdentifier:
      type: string
      description: Identifier of the document the schedule delivers, shared by the document's dashboard and workbook
      example: 12db1a0a

    DashboardName:
      type: string
      deprecated: true
      description: 'Deprecated: use `documentName` instead. Name of the document the schedule delivers.'
      example: Weekly Sales Report

    DocumentName:
      type: string
      description: 'Name of the document the schedule delivers: the dashboard, or the workbook for a workbook tab'
      example: Weekly Sales Report

    DashboardControlUpdate:
      type: object
      description: Partial update for a dashboard control
      properties:
        label:
          type: string
          description: Display label
        description:
          type: string
          description: Help text description
        hidden:
          type: boolean
          description: Whether to hide the control

    PageInfo:
      type: object
      description: Pagination information for paginated responses.
      properties:
        hasNextPage:
          type: boolean
          description: Indicates if there are more records available.
        nextCursor:
          type: string
          nullable: true
          description: Cursor for the next page of results. `null` if no more results.
        pageSize:
          type: integer
          description: Number of records per page.
        totalRecords:
          type: integer
          description: Total number of records matching the query.

    DocumentsListResponse:
      type: object
      properties:
        pageInfo:
          allOf:
            - $ref: '#/components/schemas/PageInfo'
            - description: Pagination information
        records:
          type: array
          items:
            $ref: '#/components/schemas/Document'
          description: List of documents
      required:
        - pageInfo
        - records

    Document:
      type: object
      properties:
        _count:
          type: object
          properties:
            favorites:
              type: number
              description: Number of users who favorited this document
            views:
              type: number
              description: Number of views
          required:
            - favorites
            - views
          description: "Document counts (included when `_count` is in `include` param)"
        connectionId:
          type: string
          description: Connection ID the document is associated with
        deleted:
          type: boolean
          description: Whether the document is deleted (archived)
        folder:
          $ref: '#/components/schemas/DocumentFolder'
        hasApp:
          type: boolean
          description: Whether the document has an associated app
        hasDashboard:
          type: boolean
          description: Whether the document has an associated dashboard
        identifier:
          type: string
          description: Document identifier
          example: "abc123"
        labels:
          type: array
          items:
            type: string
          description: "Labels applied to the document (included when `labels` is in `include` param)"
        lastViewedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Last time the dashboard was viewed, or null if never
        name:
          type: string
          description: Document name
        description:
          type: string
          description: Description of the document
        owner:
          $ref: '#/components/schemas/DocumentOwner'
        scope:
          type: string
          enum:
            - restricted
            - organization
          description: Document access scope
        updatedAt:
          type:
            - string
            - "null"
          format: date-time
          description: Last updated timestamp
        url:
          type: string
          description: |
            URL to view the document. If the document has a presentation (e.g., app, dashboard, etc.), returns the URL for the presentation. Otherwise, returns the workbook URL.
          example: "https://org.omni.co/dashboards/abc123"
        visits:
          type:
            - number
            - "null"
          description: "Number of visits to the document's dashboard, or to its app if it has one instead. null until the first visit."
      required:
        - connectionId
        - deleted
        - folder
        - hasApp
        - hasDashboard
        - identifier
        - name
        - owner
        - scope
        - updatedAt
        - url

    DocumentFolder:
      type:
        - object
        - "null"
      description: Folder containing the document
      properties:
        id:
          type: string
          description: Folder ID
        name:
          type: string
          description: Folder name
        path:
          type: string
          description: Folder path
        scope:
          type: string
          enum:
            - restricted
            - organization
          description: Folder access scope
      required:
        - id
        - name
        - path
        - scope

    DocumentOwner:
      type: object
      description: Document owner
      properties:
        id:
          type: string
          description: |
            The owner's user ID, as returned by the [List users](/api/users/list-users) endpoint.

            In the rare case the owner has no membership in the organization at all (for example, an embed user removed through a hard delete), this falls back to the owner's raw user ID instead.
        name:
          type: string
          description: Owner display name
      required:
        - id
        - name

    PaginatedResponse:
      type: object
      properties:
        records:
          type: array
          items:
            type: object
        pageInfo:
          $ref: '#/components/schemas/PageInfo'

    Connection:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: "c0f12353-4817-4398-bcc0-d501e6dd2f64"
          description: The connection's unique ID
        name:
          type: string
          example: "Production Database"
          description: A descriptive name for the connection
        dialect:
          type: string
          example: "postgres"
          description: The type of database
        database:
          type: string
          example: "prod_db"
          description: The default database/catalog to connect to.
        baseRole:
          type: string
          example: "NO_ACCESS"
          description: The default role for users accessing the connection.
        createdAt:
          type: string
          format: date-time
          example: "2026-06-01T14:30:00.000Z"
          description: The time the connection was created.
        updatedAt:
          type: string
          format: date-time
          example: "2026-06-11T10:15:30.000Z"
          description: The time the connection was last updated.
        deletedAt:
          type: string
          format: date-time
          example: null
          description: The time the database was deleted.
        userAttributeNameForConnectionEnvironments:
          type: string
          example: "environment"
        userAttributeValuesForDefaultEnvironment:
          type: string
        branchConnectionEnvironmentOverridesUserAttr:
          type: boolean
          example: "false"
        environmentConnectionSwitchesSchemaModel:
          type: boolean
          example: "false"
        defaultSchema:
          type: string
          example: "public"
          description: The default schema to use.

    Folder:
      type: object
      description: Represents a folder in Omni.
      properties:
        id:
          type: string
          description: Unique folder identifier
        name:
          type: string
          description: Name of the folder
          example: "My Reports"
        path:
          type: string
          description: Full path to the folder.
        scope:
          type: string
          enum: [organization, restricted]
          description: |
            Visibility scope of the folder.

            - `organization` - Organization-wide access
            - `restricted` - Limited access
        owner:
          type: object
          description: Information about the folder owner.
          properties:
            id:
              type: string
              description: ID of the folder owner.
            name:
              type: string
              description: Display name of the owner.
        labels:
          type: array
          items:
            type: string
          description: List of labels associated with the folder. Only included when requested via the `include` parameter.
        _count:
          type: object
          description: Contains count information. Only included when requested via the `include` parameter.
          properties:
            documents:
              type: integer
              description: Number of documents contained in the folder.
            favorites:
              type: integer
              description: Number of users who favorited this folder.
        url:
          type: string
          format: uri
          description: Direct link to the folder in the Omni UI.
          example: https://blobsrus.omni.co/f/blob-sales-reports
      required:
        - id
        - name
        - owner
        - scope
        - path
        - url

    Label:
      type: object
      description: A label for organizing and categorizing documents and folders
      properties:
        name:
          type: string
          description: |
            The label name (2-25 characters).

            Names are case-insensitive: `"Production"` and `"production"` are considered the same.
          example: "Production"
        verified:
          type: boolean
          description: Whether the label is verified/curated
          example: true
        homepage:
          type: boolean
          description: Whether the label appears on the organization homepage
          example: false
        usage_count:
          type: integer
          description: Total number of documents and folders using this label
          example: 12
        color:
          type: string
          maxLength: 9
          default: null
          description: |
            Hex color for the label
          example: "#0366d6"
        description:
          type: string
          maxLength: 500
          default: null
          description: Description of the label
          example: "Documents based on dev schemas"

    Model:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: ID of the model.
        baseModelId:
          type: string
          format: uuid
          nullable: true
          description: ID of the base model, if applicable.
        connectionId:
          type: string
          format: uuid
          nullable: true
          description: ID of the connection the model is based on.
        modelKind:
          type: string
          nullable: true
          description: The type of model.
        name:
          type: string
          nullable: true
          description: The name of the model.
        orgBranchId:
          type: string
          format: uuid
          nullable: true
          description: |
            ID of the branch as a whole, shared by every model layer that is a member of it. Only set when `modelKind` is `BRANCH`; `null` otherwise. Use this ID with the [Branches API](/api/branches/list-branches).
        createdAt:
          type: string
          format: date-time
          description: The time the model was created.
        updatedAt:
          type: string
          format: date-time
          description: The time the model was last updated.
        deletedAt:
          type: string
          format: date-time
          nullable: true
          description: The time the model was deleted, if applicable.
    
    BranchPartId:
      type: string
      format: uuid
      description: ID of this branch's layer on the model.

    BranchPartAllowed:
      type: boolean
      description: Whether the caller may merge this branch part.
  
    BranchPartDraftDocumentId:
      type: string
      format: uuid
      description: ID of the published document the draft belongs to.

    BranchPartDraftDocumentIdentifier:
      type: string
      description: The published document's identifier (URL slug).

    BranchPartDrafts:
      type: array
      description: Drafts this model layer contains, regardless of who wrote them.
      items:
        type: object
        properties:
          id:
            type: string
            format: uuid
            description: The draft's ID.
          documentId:
            type: string
            format: uuid
            description: ID of the published document the draft belongs to.
          documentIdentifier:
            type: string
            description: The published document's identifier (URL slug).
          modelId:
            type: string
            format: uuid
            description: ID of the base model.
          name:
            type: string
            description: The draft's name.

    BranchPartModelId:
      type: string
      format: uuid
      description: ID of the base model this layer belongs to.

    BranchPartModelName:
      type: string
      description: Name of the base model.

    BranchPartPullRequestUrl:
      type: string
      description: URL of this layer's pull request, if one exists.

    BranchPartReasonModel:
      type: string
      enum: [no_promote_rights, git_follower, pull_request_required, pr_not_merged, no_settings_rights]
      description: |
        Why the caller may not merge this branch part. Only present when `allowed` is `false`.

        | Reason | Meaning |
        |--------|---------|
        | `no_promote_rights` | The caller lacks permission to promote changes on this model. |
        | `git_follower` | The model follows Git as its source of truth; changes can only be promoted through a merged pull request. |
        | `pull_request_required` | This model requires pull requests and this layer doesn't have one. |
        | `pr_not_merged` | This layer has an open pull request that hasn't been merged yet. |
        | `no_content_edit_rights` | The caller lacks permission to edit this content in a branch. |
        | `no_settings_rights` | Reserved for modelless branch parts; not yet in use. |

    BranchPartReasonDraft:
      type: string
      enum: [no_content_edit_rights]
      description: |
        Why the caller may not merge this part. Only present when `allowed` is `false`.

        | Reason | Meaning |
        |--------|---------|
        | `no_content_edit_rights` | The caller lacks permission to edit this content in a branch. |

    BranchModelPart:
      type: object
      required: [kind, id, modelId, modelName, pullRequestUrl, allowed, drafts]
      properties:
        id:
          $ref: '#/components/schemas/BranchPartId'
        allowed:
          $ref: '#/components/schemas/BranchPartAllowed'
        drafts:
          $ref: '#/components/schemas/BranchPartDrafts'
        kind:
          type: string
          enum: [model]
          description: The kind of branch part. This will be `model`.
        modelId:
          $ref: '#/components/schemas/BranchPartModelId'
        modelName:
          $ref: '#/components/schemas/BranchPartModelName'
        pullRequestUrl:
          $ref: '#/components/schemas/BranchPartPullRequestUrl'
        reason:
          $ref: '#/components/schemas/BranchPartReasonModel'
        
    BranchPartDraft:
      type: object
      required: [kind, id, documentId, documentIdentifier, modelId, name, allowed]
      properties:
        allowed:
          $ref: '#/components/schemas/BranchPartAllowed'
        documentId:
          $ref: '#/components/schemas/BranchPartDraftDocumentId'
        documentIdentifier:
          $ref: '#/components/schemas/BranchPartDraftDocumentIdentifier'
        kind:
          type: string
          enum: [draft]
          description: The kind of branch part. This will be `draft`.
        name:
          type: string
          description: The draft's name.
        modelId:
          $ref: '#/components/schemas/BranchPartModelId'
          description: ID of the base model, which has no changes of its own.
        reason:
          $ref: '#/components/schemas/BranchPartReasonDraft'

    BranchSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: The organization-wide branch ID.
        name:
          type: string
          description: The branch's name.
        description:
          type: string
          description: The branch's description, if one was set.
        openToViewers:
          type: boolean
          description: Whether Viewers and Restricted Queriers see this branch instead of production. See [Opening a branch to viewers](/content/develop/branch-mode#opening-a-branch-to-viewers).
        createdAt:
          type: string
          format: date-time
          description: When the branch was created.
        updatedAt:
          type: string
          format: date-time
          description: When the branch was last updated.
      required:
        - id
        - name
        - openToViewers
        - createdAt
        - updatedAt

    ModelGitConfig:
      type: object
      properties:
        authMethod:
          type: string
          enum: [ssh, https_token]
          description: Authentication method. `ssh` for deploy key, `https_token` for deploy token/PAT.
          example: "ssh"
        cloneUrl:
          type: string
          description: Clone URL of the git repository (SSH or HTTPS)
          example: "git@github.com:org/repo.git"
        baseBranch:
          type: string
          description: The target branch for Omni pull requests.
          example: "main"
        branchPerPullRequest:
          type: boolean
          description: If `true`, all pull requests will create a branch in Omni, even those created outside of the tool.
          example: false
        gitFollower:
          type: boolean
          description: If `true`, the shared model is read-only and can only be updated by merging pull requests to the base branch.
          example: false
        gitServiceProvider:
          type: string
          enum: [github, gitlab, azure_devops, bitbucket]
          description: |
            The git provider type.

            - `github` - GitHub
            - `gitlab` - GitLab
            - `azure_devops` - Azure DevOps
            - `bitbucket` - Bitbucket
          example: "github"
        modelPath:
          type: string
          nullable: true
          description: Path to model files in the repository.
          example: "omni/blobs_r_us"
        publicKey:
          type: [string, null]
          description: |
            SSH public key for repository access (deploy key). Null for HTTPS token auth (`authMethod: https_token`).
          example: "ssh-ed25519 AAAA..."
        requirePullRequest:
          type: string
          enum: [always, users-only, never]
          description: |
            Controls when pull requests are required for changes:

            - `always` - Required for all changes
            - `users-only` - Required only for user-initiated changes
            - `never` - Never required
          example: "users-only"
        sshUrl:
          type: string
          deprecated: true
          description: |
            **Deprecated. Use `cloneUrl` instead**. SSH URL of the git repository.
          example: "git@github.com:org/repo.git"
        webUrl:
          type: string
          nullable: true
          description: Custom web URL for the git repository, or null if not set.
          example: "https://github.com/org/repo"
        webhookSecret:
          type: string
          description: Webhook secret for signature verification. Only included if requested via `?include=webhookSecret`.
        webhookUrl:
          type: string
          description: Webhook URL to configure in your git provider.
          example: "https://app.omni.co/api/webhooks/model/..."

    ModelsGitGetResponse:
      type: object
      properties:
        authMethod:
          type: string
          enum:
            - ssh
            - https_token
            - github_app
          description: Authentication method. "ssh" for deploy key, "https_token" for deploy token/PAT, "github_app" for a GitHub App installation.
          example: ssh
        baseBranch:
          type: string
          description: The target branch for Omni pull requests
          example: main
        branchPerPullRequest:
          type: boolean
          description: |
            If `true`, all pull requests will create a branch in Omni
          example: false
        cloneUrl:
          type: string
          description: Clone URL of the git repository (SSH or HTTPS)
          example: git@github.com:org/repo.git
        commitSigningCommitterEmail:
          type: string
          nullable: true
          description: |
            Committer email written into signed commits (`github_app` auth). Null when signing is not configured.
          example: omni-bot@example.com
        commitSigningCommitterName:
          type: string
          nullable: true
          description: |
            Committer display name written into signed commits (`github_app` auth). Null when signing is not configured.
          example: Omni Bot
        commitSigningPublicKey:
          type: string
          nullable: true
          description: |
            SSHSIG signing public key to register on the committer's GitHub user (`github_app` auth). Null for other auth methods.
          example: ssh-ed25519 AAAA...
        gitFollower:
          type: boolean
          description: |
            If `true`, the shared model is read-only and can only be updated by merging pull requests to the base branch
          example: false
        gitServiceProvider:
          type: string
          description: The git provider type
          example: github
        githubAppInstallationId:
          type: string
          nullable: true
          description: |
            GitHub App installation ID. Null unless `github_app` auth.
          example: '12345678'
        modelPath:
          type: string
          nullable: true
          description: Path to model files in the repository
          example: omni/my_model
        publicKey:
          type: string
          nullable: true
          description: SSH public key for repository access (deploy key). Null for HTTPS token auth.
          example: ssh-ed25519 AAAA...
        requirePullRequest:
          type: string
          enum:
            - always
            - users-only
            - never
          description: |
            When pull requests are required: `always` for all changes, `users-only` for user-initiated changes only, `never` for direct commits.
          example: users-only
        sshUrl:
          type: string
          deprecated: true
          description: |
            Deprecated — use `cloneUrl`. Clone URL of the git repository.
        webUrl:
          type: string
          nullable: true
          description: Custom web URL for the git repository, or null if not set
          example: https://github.com/org/repo
        webhookSecret:
          type: string
          description: |
            Webhook secret for signature verification. Only included if requested via `?include=webhookSecret`
        webhookUrl:
          type: string
          description: Webhook URL to configure in your git provider
          example: https://app.omni.co/api/webhooks/model/...
      required:
        - authMethod
        - baseBranch
        - branchPerPullRequest
        - cloneUrl
        - commitSigningCommitterEmail
        - commitSigningCommitterName
        - commitSigningPublicKey
        - gitFollower
        - gitServiceProvider
        - githubAppInstallationId
        - modelPath
        - publicKey
        - requirePullRequest
        - sshUrl
        - webUrl
        - webhookUrl

    ModelSuggestion:
      type: object
      required:
        - id
        - category
        - title
        - rationale
        - priority
        - proposedChanges
        - evidence
        - ignoreReason
        - ignoredAt
        - ignoredBy
        - aiModifiedAt
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for the suggestion.
        category:
          type: string
          description: Suggestion category, e.g. `missing_context`.
          example: "missing_context"
        title:
          type: string
          description: Short human-readable title.
        rationale:
          type: string
          description: Explanation of why the suggestion was made.
        priority:
          type: integer
          minimum: 1
          maximum: 10
          description: Priority from 1 (highest) to 10 (lowest).
          example: 1
        proposedChanges:
          $ref: '#/components/schemas/SuggestionProposedChanges'
        evidence:
          type: array
          nullable: true
          description: Source evidence for the suggestion. `null` for rows created before evidence was tracked; `[]` when none was cited.
          items:
            $ref: '#/components/schemas/SuggestionEvidenceItem'
        ignoreReason:
          type: string
          nullable: true
          description: Optional free-text reason recorded when the suggestion was dismissed.
        ignoredAt:
          type: string
          format: date-time
          nullable: true
          description: ISO 8601 timestamp of dismissal, or `null` if active.
        ignoredBy:
          type: string
          format: uuid
          nullable: true
          description: User ID that dismissed the suggestion, or `null` if active.
        aiModifiedAt:
          type: string
          format: date-time
          description: ISO 8601 timestamp of the last AI write (create or AI update). Unaffected by dismiss/restore.
        createdAt:
          type: string
          format: date-time
          description: ISO 8601 timestamp of when the suggestion was created.
        updatedAt:
          type: string
          format: date-time
          description: ISO 8601 timestamp of the last write of any kind, including dismiss/restore.

    SuggestionEvidenceItem:
      type: object
      required:
        - type
        - chatAiSessionId
        - capturedAt
      properties:
        type:
          type: string
          enum: [ai_chat]
          description: The kind of evidence. Currently always `ai_chat`.
        chatAiSessionId:
          type: string
          format: uuid
          description: Chat session that motivated the suggestion.
        capturedAt:
          type: string
          description: ISO 8601 timestamp of when the evidence was captured.

    SuggestionProposedChanges:
      type: object
      description: The change(s) the suggestion would apply to the model.
      required:
        - kind
        - edits
      properties:
        kind:
          type: string
          enum: [context_edits]
          description: The kind of change. Currently always `context_edits`.
        edits:
          type: array
          items:
            $ref: '#/components/schemas/SuggestionContextEdit'

    SuggestionRun:
      type: object
      description: Represents a single AI model suggestion generation run.
      required:
        - id
        - status
        - triggerSource
        - triggeredBy
        - createdAt
        - executionStartedAt
        - completedAt
        - error
      properties:
        id:
          type: string
          format: uuid
          description: The run ID.
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        status:
          type: string
          enum:
            - queued
            - executing
            - complete
            - failed
          description: Run status. Terminal states are `complete` and `failed`.
          example: complete
        triggerSource:
          type: string
          enum:
            - manual
            - scheduled
          description: Whether the run was triggered manually or by the schedule.
          example: manual
        triggeredBy:
          type: object
          nullable: true
          description: The user who triggered a manual run; `null` for scheduled runs.
          properties:
            userId:
              type: string
              format: uuid
              description: The user's ID.
              example: b2c3d4e5-f6a7-8901-bcde-f12345678901
            name:
              type: string
              nullable: true
              description: The user's name.
              example: Jane Doe
          required:
            - userId
            - name
        createdAt:
          type: string
          format: date-time
          description: ISO 8601 timestamp when the run was created (queued).
          example: "2026-07-15T14:30:00Z"
        executionStartedAt:
          type: string
          nullable: true
          format: date-time
          description: ISO 8601 timestamp when the worker started executing; `null` while queued.
          example: "2026-07-15T14:30:05Z"
        completedAt:
          type: string
          nullable: true
          format: date-time
          description: ISO 8601 timestamp when the run reached a terminal state.
          example: "2026-07-15T14:32:30Z"
        error:
          type: object
          nullable: true
          additionalProperties: {}
          description: Failure details when `status` is `failed`; `null` otherwise.

    WhoamiModelRole:
      type: object
      required:
        - baseRole
        - connectionId
        - permissions
        - roleName
      properties:
        baseRole:
          type: string
          description: The resolved base role. For custom roles, the base role they extend.
          example: QUERIER
        connectionId:
          type: string
          description: The connection this model belongs to
        permissions:
          type: array
          items:
            type: string
            enum:
              - QUERY_FULL_MODEL
              - QUERY_SQL
              - VIEW_SQL
              - QUERY_TOPICS
              - RUN_CONTENT_QUERIES
              - DOWNLOAD_CONTENT_QUERY
              - UPLOAD_CSV
              - SCHEDULE
              - SAVE_SPREADSHEETS
              - USE_AI
              - USE_WORKBOOKS
              - UPDATE
              - UPDATE_RESTRICTED
          description: |
            The caller''s resolved/effective permissions on this model, reflecting custom roles. This is a capability signal for the directly-roleable model kinds (schema / shared / extension). 
            
            It does not enumerate the permissions you derive on branch, workbook, and query models from your role on the base model they descend from - bsence here does not mean you lack access on those derived models.`MANAGE_MODEL`, `READ`, and `REFRESH_SCHEMA` are also not reported: they derive from connection / sibling-model roles rather than a per-model rule.
          example:
            - QUERY_TOPICS
            - QUERY_SQL
            - USE_WORKBOOKS
        roleName:
          type: string
          description: The resolved role name; may be a custom role. Use `permissions` to decide capability.
          example: QUERIER
  
    WhoamiUser:
      type: object
      required:
        - id
        - membershipId
      properties:
        id:
          type: string
          description: The caller's user ID
        membershipId:
          type: string
          description: The caller's own membership ID within this organization. This is the ID accepted by the [Get model roles endpoint](/api/user-model-roles/retrieve-user-model-roles) and is distinct from the user ID.
      
    SuggestionContextEdit:
      type: object
      required:
        - field
        - target
        - value
      properties:
        field:
          type: string
          description: The model field being edited (e.g. `ai_context`).
          example: "ai_context"
        target:
          type: string
          description: Dot-path identifying what the edit applies to, e.g. `views.orders.fields.status`.
          example: "views.orders"
        value:
          anyOf:
            - type: string
            - type: array
              items:
                type: string
          description: The proposed value for the field.

    SuggestionsCooldownResponse:
      type: object
      required:
        - detail
        - lastCompletedAt
        - retryAfterSeconds
        - status
      properties:
        detail:
          type: string
          description: Human-readable error message.
        lastCompletedAt:
          type: string
          format: date-time
          description: When the most recent run completed.
        retryAfterSeconds:
          type: integer
          description: Seconds to wait before a new run is allowed.
        status:
          type: integer
          example: 429

    Permission:
      type: object
      properties:
        role:
          type: string
          enum: [VIEWER, EDITOR, MANAGER]
        accessBoost:
          type: boolean
        direct:
          type: boolean
        inherited:
          type: boolean

    FolderPermission:
      type: object
      description: Represents a folder permission in Omni.
      properties:
        id:
          type: string
          deprecated: true
          description: Deprecated. Use `userId` or `userGroupId` instead.
        userId:
          type: string
          format: uuid
          description: The ID of the user. Only present when `type` is `user`. Not present for an organization-wide permission. Use this ID with the grant, update, and revoke folder permissions endpoints.
        userGroupId:
          type: string
          description: The ID of the user group. Only present when `type` is `userGroup`. Use this ID with the grant, update, and revoke folder permissions endpoints.
        name:
          type: string
          description: The name of the user or user group.
        description:
          type: string
          description: Description of the permission source.
        type:
          type: string
          description: The type of the permission holder.
          enum: [user, userGroup]
        direct:
          type: object
          description: Direct permission details for this folder.
          properties:
            accessBoost:
              type: boolean
              description: If `true`, [AccessBoost](/share#boosting-permissions-with-accessboost) is enabled for the user.
            isOwner:
              type: boolean
              description: If `true`, the user is the owner of the folder.
            role:
              type: string
              description: The content role associated with the user.
              enum: [NO_ACCESS, VIEWER, EDITOR, MANAGER, OWNER]

    DocumentAccessPrincipal:
      type: object
      description: Represents a user or group with access to a document.
      properties:
        id:
          type: string
          deprecated: true
          description: Deprecated. Use `userId` or `userGroupId` instead.
        userId:
          type: string
          format: uuid
          description: The ID of the user. Only present when `type` is `user`. Use this ID with the grant and revoke document permissions endpoints.
        userGroupId:
          type: string
          description: The ID of the user group. Only present when `type` is `userGroup`. Use this ID with the grant and revoke document permissions endpoints.
        name:
          type: string
          description: Display name of the user or user group.
        email:
          type: string
          description: Email address. Only present for users, not user groups.
        type:
          type: string
          enum: [user, userGroup]
          description: The type of principal.
        role:
          type: string
          enum: [VIEWER, INTERACTOR, EDITOR, MANAGER, OWNER]
          description: Permission level assigned to this principal.
        accessBoost:
          type: boolean
          description: Whether elevated access is enabled for this principal.
        accessSource:
          type: string
          enum: [direct, folder]
          description: |
            How access was granted:
            - `direct` — Explicit document permissions
            - `folder` — Inherited from folder permissions
        isOwner:
          type: boolean
          description: Whether this user owns the document. Only present for users, not user groups.
        folderInfo:
          type: object
          description: Information about the folder that grants access. Only present when `accessSource` is `folder`.
          properties:
            id:
              type: string
              description: The ID of the folder.
            name:
              type: string
              description: The name of the folder.
            path:
              type: string
              description: The full path of the folder.

    ScimGroup:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the group
        displayName:
          type: string
          description: The name of the group
        members:
          type: array
          description: The list of users in the group. Omitted when `excludedAttributes=members` is requested.
          items:
            type: object
            properties:
              value:
                type: string
                description: The user's unique identifier
              display:
                type: string
                description: The user's email address
        meta:
          type: object
          description: Metadata about the group resource
          properties:
            resourceType:
              type: string
              example: "Group"
              description: The type of resource. This will be `Group`.
            created:
              type: string
              format: date-time
              description: The time the group was created
            lastModified:
              type: string
              format: date-time
              description: The time the group was last updated
        schemas:
          type: array
          description: SCIM schema information for the group
          items:
            type: string
      required:
        - id
        - displayName
        - meta
        - schemas

    ScimUser:
      type: object
      properties:
        id:
          type: string
          description: Unique identifier for the user
        userName:
          type: string
          description: The user's email address
        displayName:
          type: string
          description: The user's display name
        active:
          type: boolean
          description: Whether the user is active
        emails:
          type: array
          description: The user's email addresses
          items:
            type: object
            properties:
              primary:
                type: boolean
                description: Whether this is the user's primary email
              value:
                type: string
                description: The email address
        groups:
          type: array
          description: User groups that the user belongs to
          items:
            type: object
            properties:
              display:
                type: string
                description: The group's display name
              value:
                type: string
                description: The group's unique identifier
        meta:
          type: object
          properties:
            resourceType:
              type: string
              example: "User"
              description: The resource type. This will be `User`.
            created:
              type: string
              format: date-time
              description: The time the user was created
            lastModified:
              type: string
              format: date-time
              description: The time the user was last updated
        schemas:
          type: array
          items:
            type: string
          description: |
            SCIM information about the type of schemas used in the API. For example, `urn:ietf:params:scim:schemas:core:2.0:User`
        urn:omni:params:1.0:UserAttribute:
          type: object
          description: |
            [User attributes](/administration/users/attributes) as key/value pairs, where keys map to the IDs of user attributes defined in Omni. This is the **Reference** column in the **User attributes** page. A value is a string, a number, an array of strings or an array of numbers for a multi-value attribute, or `null`. Numbers appear only for legacy values stored before Omni started saving attribute values as strings.
        urn:omni:params:scim:schemas:extension:user:2.0:
          type: object
          description: Omni SCIM extension schema containing additional user metadata
          properties:
            lastLogin:
              type: string
              format: date-time
              description: The timestamp of the user's last login. Will be `null` if the user has never logged in.
            userType:
              type: string
              enum:
                - Admin
                - External
                - Member
              description: The user's type. `Admin` is the Organization Admin role.
            emailLinkSignIn:
              type: boolean
              description: |
                Whether the user can sign in with an [email link](/administration/settings/authentication#email) while the organization requires SSO.
          required:
            - userType
            - emailLinkSignIn
      required:
        - active
        - displayName
        - emails
        - groups
        - id
        - meta
        - schemas
        - userName
        - urn:omni:params:1.0:UserAttribute
        - urn:omni:params:scim:schemas:extension:user:2.0

    ScimEmbedUser:
      type: object
      description: An embed user object (SCIM 2.0)
      properties:
        id:
          type: string
          description: Unique identifier for the user
        userName:
          type: string
          description: The user's email address
        displayName:
          type: string
          description: The user's display name
        active:
          type: boolean
          description: Whether the user is active
        emails:
          type: array
          description: The user's email addresses
          items:
            type: object
            properties:
              primary:
                type: boolean
                description: Whether this is the user's primary email
              value:
                type: string
                description: The email address
        groups:
          type: array
          description: User groups that the user belongs to
          items:
            type: object
            properties:
              display:
                type: string
                description: The group's display name
              value:
                type: string
                description: The group's unique identifier
        meta:
          type: object
          properties:
            resourceType:
              type: string
              example: "User"
              description: The type of resource. This will be `User`.
            created:
              type: string
              format: date-time
              description: The time the user was created
            lastModified:
              type: string
              format: date-time
              description: The time the user was last uodated
        schemas:
          type: array
          items:
            type: string
          description: |
            SCIM information about the type of schemas used in the API. For example, `urn:ietf:params:scim:schemas:core:2.0:User`
        urn:omni:params:1.0:UserAttribute:
          type: object
          description: |
            [User attributes](/administration/users/attributes) as key/value pairs, where keys map to the IDs of user attributes defined in Omni. This is the **Reference** column in the **User attributes** page. A value is a string, a number, an array of strings or an array of numbers for a multi-value attribute, or `null`. Numbers appear only for legacy values stored before Omni started saving attribute values as strings.
        urn:omni:params:scim:schemas:extension:user:2.0:
          type: object
          description: Omni SCIM extension schema containing additional user metadata
          properties:
            lastLogin:
              type: string
              format: date-time
              nullable: true
              description: The timestamp of the user's last login. Will be `null` if the user has never logged in.
            userType:
              type: string
              enum:
                - Admin
                - External
                - Member
              description: The user's type. `Admin` is the Organization Admin role.
            emailLinkSignIn:
              type: boolean
              description: Whether the user can sign in with an email link while the organization requires SSO.
          required:
            - userType
            - emailLinkSignIn
        embedEmail:
          type: string
          nullable: true
          description: The embed user's email address
        embedEntity:
          type: string
          nullable: true
          description: The embed entity identifier
        embedExternalId:
          type: string
          nullable: true
          description: The external ID for the embed user
      required:
        - active
        - displayName
        - emails
        - groups
        - id
        - meta
        - schemas
        - userName
        - urn:omni:params:1.0:UserAttribute
        - urn:omni:params:scim:schemas:extension:user:2.0

    EvalPrompt:
      type: object
      properties:
        created_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the prompt was created.
          example: '2025-01-15T10:00:00.000Z'
        expectation:
          type:
            - string
            - 'null'
          description: The expectation the analysis judge scores the analysis against, or null when none was set.
          example: The top product by revenue should be Aniseed Syrup.
        id:
          type: string
          format: uuid
          description: Unique identifier for the prompt.
          example: 770e8400-e29b-41d4-a716-446655440002
        prompt_text:
          type: string
          description: The natural language prompt text the AI is evaluated on.
          example: What are the top 5 products by revenue?
        updated_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the prompt was last updated.
          example: '2025-01-15T10:00:00.000Z'
      required:
        - created_at
        - expectation
        - id
        - prompt_text
        - updated_at
    EvalPromptSet:
      type: object
      properties:
        created_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the prompt set was created.
          example: '2025-01-15T10:00:00.000Z'
        description:
          type:
            - string
            - 'null'
          description: Optional human-readable description of the prompt set.
          example: Regression suite for the orders topic
        id:
          type: string
          format: uuid
          description: Unique identifier for the prompt set.
          example: 550e8400-e29b-41d4-a716-446655440000
        is_archived:
          type: boolean
          description: Whether the prompt set has been archived.
          example: false
        model_id:
          type: string
          format: uuid
          description: The shared model this prompt set is bound to.
          example: 880e8400-e29b-41d4-a716-446655440003
        name:
          type: string
          description: Human-readable name for the prompt set.
          example: Orders regression
        prompts:
          type: array
          items:
            $ref: '#/components/schemas/EvalPrompt'
          description: Prompts that make up the set.
        slug:
          type: string
          description: URL-safe identifier for the prompt set. Unique per `model_id`.
          example: orders-regression
        updated_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the prompt set was last updated.
          example: '2025-01-15T10:00:00.000Z'
      required:
        - created_at
        - description
        - id
        - is_archived
        - model_id
        - name
        - prompts
        - slug
        - updated_at
    EvalPromptSetListItem:
      type: object
      properties:
        created_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the prompt set was created.
          example: '2025-01-15T10:00:00.000Z'
        description:
          type:
            - string
            - 'null'
          description: Optional human-readable description of the prompt set.
          example: Regression suite for the orders topic
        id:
          type: string
          format: uuid
          description: Unique identifier for the prompt set.
          example: 550e8400-e29b-41d4-a716-446655440000
        is_archived:
          type: boolean
          description: Whether the prompt set has been archived.
          example: false
        model_id:
          type: string
          format: uuid
          description: The shared model this prompt set is bound to.
          example: 880e8400-e29b-41d4-a716-446655440003
        name:
          type: string
          description: Human-readable name for the prompt set.
          example: Orders regression
        slug:
          type: string
          description: URL-safe identifier for the prompt set. Unique per `model_id`.
          example: orders-regression
        updated_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the prompt set was last updated.
          example: '2025-01-15T10:00:00.000Z'
        latest_run_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp of the most recent run on this prompt set, if any.
          example: '2025-01-15T10:05:00.000Z'
        prompt_count:
          type: integer
          description: Number of prompts in the set.
          example: 12
      required:
        - created_at
        - description
        - id
        - is_archived
        - model_id
        - name
        - slug
        - updated_at
        - latest_run_at
        - prompt_count
    EvalPromptSetsCreateBody:
      type: object
      properties:
        description:
          type:
            - string
            - 'null'
          maxLength: 1024
          description: Optional human-readable description of the prompt set. Max 1024 characters.
          example: Regression suite for the orders topic
        model_id:
          type: string
          format: uuid
          description: The shared model this prompt set is bound to.
          example: 880e8400-e29b-41d4-a716-446655440003
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Human-readable name for the prompt set. 255 characters or fewer.
          example: Orders regression
        prompts:
          type: array
          items:
            type: object
            properties:
              expectation:
                type:
                  - string
                  - 'null'
                maxLength: 16000
                description: Optional expectation the analysis judge scores the analysis against. Max 16000 characters.
                example: The top product by revenue should be Aniseed Syrup.
              prompt_text:
                type: string
                minLength: 1
                maxLength: 8000
                description: The natural language prompt text. Max 8000 characters.
                example: What are the top 5 products by revenue?
            required:
              - prompt_text
          maxItems: 25
          default: []
          description: Initial prompts for the set. Defaults to an empty list. At most 25 prompts.
        slug:
          type: string
          maxLength: 255
          pattern: ^[a-z][a-z0-9-]*$
          description: URL-safe identifier for the prompt set. Must be unique per `model_id` and match `^[a-z][a-z0-9-]*$`. Max 255 characters.
          example: orders-regression
      required:
        - model_id
        - name
        - slug
    EvalPromptSetsCreateResponse:
      type: object
      properties:
        prompt_set:
          $ref: '#/components/schemas/EvalPromptSet'
      required:
        - prompt_set
    EvalPromptSetsDeleteResponse:
      type: object
      properties:
        cancelled_job_count:
          type: integer
          description: Number of in-flight agentic jobs associated with this prompt set that were cancelled as part of the archive.
          example: 0
        is_archived:
          type: boolean
          enum:
            - true
          description: Always `true` on success — archives the prompt set.
      required:
        - cancelled_job_count
        - is_archived
    EvalPromptSetsGetResponse:
      type: object
      properties:
        prompt_set:
          $ref: '#/components/schemas/EvalPromptSet'
      required:
        - prompt_set
    EvalPromptSetsListResponse:
      type: object
      properties:
        prompt_sets:
          type: array
          items:
            $ref: '#/components/schemas/EvalPromptSetListItem'
          description: Prompt sets matching the query, sorted alphabetically by name.
      required:
        - prompt_sets
    EvalPromptSetsUnarchiveResponse:
      type: object
      properties:
        prompt_set:
          $ref: '#/components/schemas/EvalPromptSet'
      required:
        - prompt_set
    EvalPromptSetsUpdateBody:
      type: object
      properties:
        description:
          type:
            - string
            - 'null'
          maxLength: 1024
          description: New description for the prompt set. Pass `null` to clear. Max 1024 characters.
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: New human-readable name for the prompt set. 255 characters or fewer.
        prompts:
          type: array
          items:
            type: object
            properties:
              expectation:
                type:
                  - string
                  - 'null'
                maxLength: 16000
                description: Optional expectation the analysis judge scores the analysis against. Pass `null` to clear. Max 16000 characters.
                example: The top product by revenue should be Aniseed Syrup.
              id:
                type: string
                format: uuid
                description: Existing prompt id. When provided, updates that prompt; when omitted, a new prompt is created. Prompts not included in this list are removed.
              prompt_text:
                type: string
                minLength: 1
                maxLength: 8000
                description: Updated or new prompt text. Max 8000 characters.
                example: What are the top 10 products by revenue this quarter?
            required:
              - prompt_text
          maxItems: 25
          description: Full desired set of prompts after the update. Prompts omitted from this list are deleted; new prompts (no `id`) are appended in body order. Existing prompts retain their original position — reordering is not supported on this endpoint. At most 25 prompts total.
    EvalPromptSetsUpdateResponse:
      type: object
      properties:
        prompt_set:
          $ref: '#/components/schemas/EvalPromptSet'
      required:
        - prompt_set
    EvalRunDetail:
      type: object
      properties:
        branch_id:
          type:
            - string
            - 'null'
          format: uuid
          description: Optional branch ID the run was executed against. Null when run against the main shared model.
          example: null
        branch_name:
          type:
            - string
            - 'null'
          description: Display name for the branch, if `branch_id` is set.
          example: null
        completed_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the run reached a terminal state.
          example: null
        cost_basis:
          type:
            - string
            - 'null'
          enum:
            - CREDITS
            - LIST_PRICE
            - null
          description: |
            Pricing unit of the results:
            
            - `CREDITS` for Omni-managed providers
            - `LIST_PRICE` for an organization''s own provider key (an estimate at provider list price)
            - Null for runs without a resolved LLM
          example: CREDITS
        created_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the run was created.
          example: '2025-01-15T10:00:00.000Z'
        credit_downgraded:
          type: boolean
          description: Whether any job in this run was clamped to the LLM Omni uses for light tasks in the family by an organization or user [credit downgrade](/ai/settings/usage#downgrade). The run still reports its `llm` as the LLM it was created to run on.
          example: false
        description:
          type:
            - string
            - 'null'
          description: Optional human-readable description for the run.
          example: null
        id:
          type: string
          format: uuid
          description: Unique identifier for the run.
          example: 660e8400-e29b-41d4-a716-446655440001
        is_archived:
          type: boolean
          description: Whether the run has been archived.
          example: false
        llm:
          type: object
          description: The LLM this run executed on, resolved when the run was created. Reflects `run_config.llm` when the run was pinned to a specific LLM, or the organization default otherwise.
          properties:
            family_rank:
              type: string
              enum:
                - fastest
                - standard
                - smartest
              description: This LLM's rank within its provider family.
              example: standard
            model:
              type: string
              description: The model's catalog name on an Omni-hosted provider, or the id an admin entered for a bring-your-own-key provider.
              example: claude-sonnet-5
            model_id:
              type:
                - string
                - 'null'
              description: The underlying model identifier the provider served the run on, if it differs from `model`.
              example: null
            provider:
              type: string
              enum:
                - bedrock
                - anthropic
                - openai
                - grok
                - snowflake_cortex
                - openai_compatible
                - fireworks
                - fireworks_managed
                - databricks_genie
                - databricks_managed
                - baseten_managed
                - vertex_ai
              example: bedrock
            thinking:
              type: string
              enum:
                - default
                - none
                - low
                - medium
                - high
              description: The thinking level pinned on the run, or resolved from the organization default when the run didn't pin one.
              example: medium
            effort:
              type:
                - string
                - 'null'
              description: The reasoning effort actually sent to the provider, only present where it differs from `thinking`.
              example: null
          required:
            - family_rank
            - model
            - model_id
            - provider
            - thinking
            - effort
        llm_fallback_disabled:
          type: boolean
          description: Whether backup-LLM fallback was disabled for this run. Such runs fail on provider errors that ordinary runs survive, so a higher failure rate does not necessarily indicate lower LLM quality.
          example: false
        model_id:
          type: string
          format: uuid
          description: The shared model this run was executed against.
          example: 880e8400-e29b-41d4-a716-446655440003
        prompt_set_id:
          type: string
          format: uuid
          description: The prompt set this run was created from.
          example: 550e8400-e29b-41d4-a716-446655440000
        results:
          type: array
          items:  # EvalRunResult
            type: object
            properties:
              agentic_job:  # EvalRunResultAgenticJob
                type: object
                properties:
                  conversation_id:
                    type:
                      - string
                      - 'null'
                    format: uuid
                    description: Conversation the agentic job belongs to.
                    example: 770e8400-e29b-41d4-a716-446655440002
                  id:
                    type: string
                    format: uuid
                    description: Agentic job identifier.
                    example: 990e8400-e29b-41d4-a716-446655440004
                  state:
                    type: string
                    enum:
                      - CANCELLED
                      - COMPLETE
                      - DELIVERING
                      - EXECUTING
                      - FAILED
                      - QUEUED
                    description: Current state of the agentic job that ran this prompt.
                    example: COMPLETE
                required:
                  - conversation_id
                  - id
                  - state
              cost:
                type:
                  - number
                  - 'null'
                description: Total LLM cost (USD) for this prompt, if available.
                example: 0.0021
              error_reason:
                type:
                  - string
                  - 'null'
                description: |
                  Reason string for prompts whose underlying job failed, whose job completed without producing an answer (no message and no tool call), or whose answer the accuracy judge itself failed to score. A reason prefixed `judge error` means the job produced an answer but scoring it failed — for example, a judge outage, rate limit, or malformed verdict — which is distinct from the underlying job failing.
                example: null
              id:
                type: string
                format: uuid
                description: Unique identifier for the run result row.
                example: aa0e8400-e29b-41d4-a716-446655440005
              limit_kind:
                type:
                  - string
                  - 'null'
                enum:
                  - STEP_CAP
                  - WALL_CLOCK
                  - null
                description: |
                  Which limit stopped the job when `outcome` is `LIMIT_REACHED`: 
                  
                  - `STEP_CAP` for the agent step cap
                  - `WALL_CLOCK` for the query wall-clock limit 
                  - `Null` otherwise
                example: null
              outcome:
                type:
                  - string
                  - 'null'
                enum:
                  - CANCELLED
                  - JOB_FAILED
                  - JUDGE_ERROR
                  - LIMIT_REACHED
                  - NO_TEXT
                  - null
                description: |
                  How the job ended, or whether scoring did not complete:
                  
                  - `JOB_FAILED` - The job failed or never ran
                  - `LIMIT_REACHED` - The job hit a limit (see `limit_kind`).
                  - `CANCELLED` - The job was cancelled before it delivered a result, either by cancelling the run or by cancelling the job itself
                  - `NO_TEXT` - The job completed without a response
                  - `JUDGE_ERROR` - Scoring did not complete for this result
                  - `Null` while pending, when the job completed and was scored, and on results recorded before outcomes existed
                example: null
              prompt:
                type: string
                description: The prompt text that was evaluated.
                example: What are the top 5 products by revenue?
              score:
                type:
                  - number
                  - 'null'
                description: |
                  Numeric judge score for this prompt result, if scoring ran. Set to `0` without judging when the underlying job completed but produced no answer.
                example: 0.9
              scoring_cost:
                type:
                  - number
                  - 'null'
                description: Total LLM cost (USD) for scoring this prompt result.
                example: 0.0004
              timing_ms:
                type:
                  - integer
                  - 'null'
                description: Wall-clock duration of the underlying job in milliseconds.
                example: 4321
            required:
              - agentic_job
              - cost
              - error_reason
              - id
              - prompt
              - score
              - scoring_cost
              - timing_ms
          description: Per-prompt results for this run, ordered by their creation order in the prompt set.
        run_number:
          type: integer
          description: Sequential, per-prompt-set run number.
          example: 3
        status:
          type: string
          enum:
            - RUNNING
            - COMPLETE
            - CANCELLED
          description: Run-level lifecycle. Flips to a terminal state (COMPLETE or CANCELLED) exactly once.
          example: RUNNING
      required:
        - branch_id
        - branch_name
        - completed_at
        - created_at
        - credit_downgraded
        - description
        - id
        - is_archived
        - llm
        - llm_fallback_disabled
        - model_id
        - prompt_set_id
        - results
        - run_number
        - status
      description: The newly created run with its initial results.
    EvalRunListItem:
      type: object
      properties:
        branch_id:
          type:
            - string
            - 'null'
          format: uuid
          description: Optional branch ID the run was executed against. Null when run against the main shared model.
          example: null
        branch_name:
          type:
            - string
            - 'null'
          description: Display name for the branch, if `branch_id` is set.
          example: null
        completed_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the run reached a terminal state.
          example: null
        created_at:
          type:
            - string
            - 'null'
          description: ISO 8601 timestamp when the run was created.
          example: '2025-01-15T10:00:00.000Z'
        description:
          type:
            - string
            - 'null'
          description: Optional human-readable description for the run.
          example: null
        id:
          type: string
          format: uuid
          description: Unique identifier for the run.
          example: 660e8400-e29b-41d4-a716-446655440001
        is_archived:
          type: boolean
          description: Whether the run has been archived.
          example: false
        model_id:
          type: string
          format: uuid
          description: The shared model this run was executed against.
          example: 880e8400-e29b-41d4-a716-446655440003
        prompt_set_id:
          type: string
          format: uuid
          description: The prompt set this run was created from.
          example: 550e8400-e29b-41d4-a716-446655440000
        run_number:
          type: integer
          description: Sequential, per-prompt-set run number.
          example: 3
        stats:
          $ref: '#/components/schemas/EvalRunStats'
        status:
          type: string
          enum:
            - RUNNING
            - COMPLETE
            - CANCELLED
          description: Run-level lifecycle. Flips to a terminal state (COMPLETE or CANCELLED) exactly once.
          example: RUNNING
      required:
        - branch_id
        - branch_name
        - completed_at
        - created_at
        - description
        - id
        - is_archived
        - model_id
        - prompt_set_id
        - run_number
        - stats
        - status
    EvalRunStats:
      type: object
      properties:
        terminal:
          type: integer
          description: Number of per-prompt jobs that have reached a terminal state (COMPLETE, FAILED, or CANCELLED).
          example: 8
        total:
          type: integer
          description: Total number of per-prompt jobs in the run.
          example: 12
      required:
        - terminal
        - total
    EvalRunsCancelResponse:
      type: object
      properties:
        cancelled:
          type: integer
          description: Number of per-prompt agentic jobs that were cancelled by this request.
          example: 4
        run:
          allOf:
            - $ref: '#/components/schemas/EvalRunDetail'
            - description: 'The cancelled run. `status: CANCELLED` and `is_archived: true` after this call.'
        total:
          type: integer
          description: Total number of per-prompt jobs in the run.
          example: 12
      required:
        - cancelled
        - run
        - total
    EvalRunsCreateBody:
      type: object
      properties:
        description:
          type:
            - string
            - 'null'
          maxLength: 1024
          description: Optional human-readable description for the run. Omit or pass `null` to leave it unset. Max 1024 characters.
          example: Re-running after switching to gpt-4o for query generation
        prompt_set_id:
          type: string
          format: uuid
          description: The prompt set to execute.
          example: 550e8400-e29b-41d4-a716-446655440000
        run_config:
          type: object
          properties:
            branch_id:
              type: string
              format: uuid
              description: Optional branch ID to run against. Must be a branch of the prompt set's model.
              example: 440e8400-e29b-41d4-a716-446655440006
            disable_llm_fallback:
              type: boolean
              description: When true, fail the eval answer instead of retrying a provider call on a backup LLM, including calls inside validators, tools and subagents. Defaults to false. Same-LLM retries are unaffected.
              example: false
            llm:
              type: object
              properties:
                model:
                  type: string
                  minLength: 1
                  maxLength: 512
                provider:
                  type: string
                  enum:
                    - bedrock
                    - anthropic
                    - openai
                    - grok
                    - snowflake_cortex
                    - openai_compatible
                    - fireworks
                    - fireworks_managed
                    - databricks_genie
                    - databricks_managed
                    - baseten_managed
                    - vertex_ai
                thinking:
                  type: string
                  enum:
                    - default
                    - none
                    - low
                    - medium
                    - high
              required:
                - model
                - provider
              description: Optional LLM to run every prompt on, as `{ provider, model, thinking? }`. Must be one the organization has approved under AI Hub → LLM settings; omit to follow the organization default. `model` is the model's catalog name on an Omni-hosted provider (e.g. `claude-sonnet-5`) or the id the admin entered for a bring-your-own-key provider. `thinking` is one of `default`, `none`, `low`, `medium`, `high`; which of them an LLM honors depends on the LLM, and one it does not is rejected. Omit `thinking` to run at the organization default's level where the LLM honors it, else `default`; either way the level is fixed when the run is created.
              example:
                model: claude-sonnet-5
                provider: bedrock
                thinking: medium
          description: Per-run configuration. Optional — omit if no overrides.
      required:
        - prompt_set_id
    EvalRunsCreateResponse:
      type: object
      properties:
        job_count:
          type: integer
          description: Number of per-prompt agentic jobs created for this run (one per prompt that fanned out successfully). Enqueue onto the work queue happens after creation and is best-effort, so this count reflects jobs created, not necessarily those successfully enqueued.
          example: 12
        run:
          $ref: '#/components/schemas/EvalRunDetail'
      required:
        - job_count
        - run
    EvalRunsDeleteResponse:
      type: object
      properties:
        is_archived:
          type: boolean
          enum:
            - true
          description: Always `true` on success — the run has been archived.
      required:
        - is_archived
    EvalRunsGetResponse:
      type: object
      properties:
        run:
          $ref: '#/components/schemas/EvalRunDetail'
      required:
        - run
    EvalRunsListResponse:
      type: object
      properties:
        runs:
          type: array
          items:
            $ref: '#/components/schemas/EvalRunListItem'
          description: Runs for the prompt set, newest first, filtered to those whose model the caller can access.
      required:
        - runs
    EvalRunsUnarchiveResponse:
      type: object
      properties:
        is_archived:
          type: boolean
          enum:
            - false
          description: Always `false` on success — the run has been unarchived.
      required:
        - is_archived
    EvalApiError:
      type: object
      description: Error response returned by the AI Eval endpoints.
      properties:
        detail:
          type: string
          description: Human-readable error message describing what went wrong.
        status:
          type: integer
          description: HTTP status code of the error.
      required:
        - detail
        - status

    # =========================================================================
    # AI Routines
    # =========================================================================
    Routine:
      type: object
      properties:
        branchId:
          type: string
          nullable: true
          format: uuid
          description: Branch of the shared model the prompt runs against, or null.
        condition:
          $ref: '#/components/schemas/RoutineCondition'
        createdAt:
          type: string
          description: ISO 8601 timestamp when the routine was created.
        description:
          type: string
          nullable: true
          description: Display-only notes about the routine, or null.
        destination:
          $ref: '#/components/schemas/RoutineDestinationResponse'
        disabled:
          type: boolean
          description: Whether the owner has paused the routine.
        id:
          type: string
          format: uuid
          description: The unique identifier of the routine.
        lastRun:
          $ref: '#/components/schemas/RoutineLastRun'
        modelId:
          type: string
          format: uuid
          description: The shared model the prompt runs against.
        name:
          type: string
          description: Customer-visible name of the routine, used as the email subject.
        prompt:
          type: string
          description: Natural language prompt Omni runs on each scheduled run.
        recipientCount:
          type: integer
          description: Number of distinct deliverable recipients after expanding user groups and removing duplicates.
        schedule:
          type: string
          description: Six-field cron expression (minute, hour, day-of-month, month, day-of-week, year; use `?` for an unspecified day field).
        systemDisabled:
          type: boolean
          description: Whether Omni disabled the routine because it could no longer run successfully or safely.
        systemDisabledReason:
          type: string
          nullable: true
          description: Reason Omni disabled the routine, or null.
        timezone:
          type: string
          description: IANA timezone identifier used to evaluate the schedule.
        topicName:
          type: string
          nullable: true
          description: Topic scoping query generation, or null.
        updatedAt:
          type: string
          description: ISO 8601 timestamp when the routine was last updated.
      required:
        - branchId
        - condition
        - createdAt
        - description
        - destination
        - disabled
        - id
        - lastRun
        - modelId
        - name
        - prompt
        - recipientCount
        - schedule
        - systemDisabled
        - systemDisabledReason
        - timezone
        - topicName
        - updatedAt
    
    RoutineCondition:
      type: object
      nullable: true
      description: The delivery condition. Null for a routine that delivers on every scheduled run.
      properties:
        conditionPrompt:
          type: string
          nullable: true
          description: The routine's delivery condition, in plain language.
          example: there are failed orders
        conditionType:
          type: string
          enum:
            - RESULTS_CHANGED
            - RESULTS_UNCHANGED
            - RESULTS_PRESENT
            - RESULTS_MISSING
          description: |
            How Omni evaluates the condition:
            
            - `RESULTS_PRESENT` - Delivers when the condition query returns rows
            - `RESULTS_MISSING` - Condition query doesn't return rows
            - `RESULTS_CHANGED` - Condition query's results differ from the previous scheduled run
            - `RESULTS_UNCHANGED` - Condition query's results are unchanged from the previous scheduled run
          example: RESULTS_PRESENT
      required:
        - conditionPrompt
        - conditionType
  
    RoutineConditionDetail:
      type: object
      nullable: true
      description: The delivery condition and its persisted semantic query, or null for a routine that delivers on every scheduled run.
      properties:
        conditionPrompt:
          type: string
          nullable: true
          description: The routine's delivery condition, in plain language. Null for routines created before Omni stored delivery-condition text.
          example: there are failed orders
        conditionType:
          type: string
          enum:
            - RESULTS_CHANGED
            - RESULTS_UNCHANGED
            - RESULTS_PRESENT
            - RESULTS_MISSING
          description: |
            How Omni evaluates the condition:
            
            - `RESULTS_PRESENT` - Delivers when the condition query returns rows
            - `RESULTS_MISSING` - Condition query doesn't return rows
            - `RESULTS_CHANGED` - Condition query's results differ from the previous scheduled run
            - `RESULTS_UNCHANGED` - Condition query's results are unchanged from the previous scheduled run
          example: RESULTS_PRESENT
        query:
          allOf:
            - $ref: '#/components/schemas/RoutineConditionQuery'
            - description: The persisted semantic query Omni stores and evaluates for this condition. This can be inspected or passed to the [Run query API](/api/queries/run-query).
      required:
        - conditionPrompt
        - conditionType
        - query

    RoutineConditionQuery:
      type: object
      description: |
        The generated semantic query Omni stores and evaluates for this condition. This can be inspected or passed to the [Run query API](/api/queries/run-query).
      properties:
        fields:
          type: array
          items:
            type: string
          description: Ordered list of fully qualified field names to include in the query (e.g., "view_name.field_name").
          example:
            - products.name
            - order_items.total_revenue
        filters:
          type: object
          additionalProperties: {}
          description: Filter conditions keyed by fully qualified field name. Filter values vary by field type.
        limit:
          type: integer
          description: Maximum number of rows to return.
          example: 500
        sorts:
          type: array
          items:
            $ref: '#/components/schemas/AiQuerySort'
          description: Sort specifications applied to the query results.
        table:
          type: string
          description: The base topic or view name for the query.
          example: order_items
      required:
        - fields
      additionalProperties: {}
      example:
        aiGenerated: true
        calculations: []
        column_totals: {}
        fields:
          - orders.id
        fill_fields: []
        filters:
          orders.status:
            case_insensitive: true
            kind: EQUALS
            type: string
            values:
              - failed
        limit: 1
        modelId: 770e8400-e29b-41d4-a716-446655440002
        pivots: []
        row_totals: {}
        sorts: []
        table: orders
        userEditedSQL: |-
          SELECT ${orders.id}
          FROM ${orders}
          WHERE UPPER(${orders.status}) = UPPER('failed')
          LIMIT 1
        version: 9

    RoutineEmailDestination:
      type: object
      required:
        - type
      additionalProperties: false
      description: Email delivery configuration for the routine.
      properties:
        recipientEmails:
          $ref: '#/components/schemas/RoutineEmailRecipientEmails'
        type:
          $ref: '#/components/schemas/RoutineEmailType'
        userGroupIds:
          $ref: '#/components/schemas/RoutineEmailUserGroupIds'

    RoutineEmailRecipientEmails:
      type: array
      items:
        type: string
        format: email
      maxItems: 100
      default: []
      description: Email addresses that receive each scheduled run of the routine.
      example:
        - blob.ross@blobsrus.com
        - blob.the.builder@blobsrus.com

    RoutineEmailType:
      type: string
      enum:
        - email
      description: Selects email delivery — each scheduled run is sent to the listed email recipients and user groups.
      example: email

    RoutineEmailUserGroupIds:
      type: array
      items:
        type: string
        minLength: 1
      maxItems: 100
      default: []
      description: User group IDs whose active members receive each scheduled run. Omni expands each group to the members' current email addresses when the routine runs.
      example:
        - X9kQ2mPz

    RoutineSlackDestination:
      type: object
      required:
        - slackRecipientType
        - type
      additionalProperties: false
      description: |
        Slack delivery configuration for the routine. Requires a Slack workspace connected to Omni; to deliver to a private channel, invite the Omni Slackbot to it first.

        A request provides exactly one of `recipients` or the deprecated `recipientId`.
      properties:
        recipientId:
          $ref: '#/components/schemas/RoutineSlackRecipientId'
          description: |
            **Deprecated**. Use `recipients` instead.
        recipients:
          $ref: '#/components/schemas/RoutineSlackRecipients'
        slackRecipientType:
          $ref: '#/components/schemas/RoutineSlackRecipientType'
        type:
          $ref: '#/components/schemas/RoutineSlackType'

    RoutineSlackRecipientId:
      type: string
      minLength: 1
      deprecated: true

    RoutineSlackRecipients:
      anyOf:
        - type: string
          minLength: 1
        - type: array
          items:
            type: string
            minLength: 1
          minItems: 1
      description: |
        The Slack channel ID (e.g. `C01234567`) or user ID (e.g. `U01234567`) that receives each scheduled run, as a string or an array. Exactly one recipient per Slack routine.
      example: C01234567

    RoutineSlackRecipientType:
      type: string
      enum:
        - channel
        - users
      description: The type of Slack recipient. Use `channel` to deliver to a single Slack channel, or `users` to deliver to one or more Slack users via direct message.
      example: channel

    RoutineSlackType:
      type: string
      enum:
        - slack
      description: Selects Slack delivery — each scheduled run is posted to one Slack channel or sent as a direct message to one user.
      example: slack

    RoutineConditionVerification:
      type: object
      description: What Omni observed when it verified the condition query during creation.
      properties:
        conditionMetNow:
          type: boolean
          description: |
            Whether the condition held when Omni verified it. Present only for `RESULTS_PRESENT` and `RESULTS_MISSING`.
          example: false
        rowCount:
          type: integer
          description: Rows the condition query returned when Omni verified it.
          example: 0
      required:
        - rowCount

    RoutineDestination:
      oneOf:
        - $ref: '#/components/schemas/RoutineEmailDestination'
        - $ref: '#/components/schemas/RoutineSlackDestination'
      discriminator:
        propertyName: type
        mapping:
          email: '#/components/schemas/RoutineEmailDestination'
          slack: '#/components/schemas/RoutineSlackDestination'
      description: Single delivery destination for the routine — email or Slack. To send results to multiple destinations, create one routine per destination.

    RoutineEmailDestinationResponse:
      type: object
      required:
        - recipientEmails
        - type
        - userGroupIds
      additionalProperties: false
      description: Email delivery configuration for the routine, as stored.
      properties:
        recipientEmails:
          $ref: '#/components/schemas/RoutineEmailRecipientEmails'
        type:
          $ref: '#/components/schemas/RoutineEmailType'
        userGroupIds:
          $ref: '#/components/schemas/RoutineEmailUserGroupIds'

    RoutineSlackDestinationResponse:
      type: object
      required:
        - channelIds
        - recipientId
        - slackRecipientType
        - type
        - userIds
      additionalProperties: false
      description: Slack delivery configuration for the routine, as stored.
      properties:
        channelIds:
          type: array
          items:
            type: string
          description: The Slack channel that receives each scheduled run. Empty when the routine delivers to a user.
          example:
            - C01234567
        recipientId:
          $ref: '#/components/schemas/RoutineSlackRecipientId'
          description: |
            **Deprecated**. Use `channelIds` or `userIds`, whichever is not null.
        slackRecipientType:
          type: string
          enum:
            - channel
            - users
          deprecated: true
          description: |
            **Deprecated**. `channelIds` being non-empty means a channel. Whether `recipientId` is a Slack channel or a user (delivered as a direct message).
          example: channel
        type:
          $ref: '#/components/schemas/RoutineSlackType'
        userIds:
          type: array
          items:
            type: string
          description: The Slack user that receives each scheduled run as a direct message. Empty when the routine delivers to a channel.
          example: []

    RoutineDestinationResponse:
      oneOf:
        - $ref: '#/components/schemas/RoutineEmailDestinationResponse'
        - $ref: '#/components/schemas/RoutineSlackDestinationResponse'
      discriminator:
        propertyName: type
        mapping:
          email: '#/components/schemas/RoutineEmailDestinationResponse'
          slack: '#/components/schemas/RoutineSlackDestinationResponse'
      description: Single delivery destination for the routine, as stored — email or Slack.

    RoutineLastRun:
      type: object
      nullable: true
      required:
        - completedAt
        - label
        - state
      description: Most recent completed run, or null if the routine has never completed a run.
      properties:
        completedAt:
          type: string
          nullable: true
          description: ISO 8601 timestamp the last completed run finished.
        label:
          type: string
          description: User-visible status of the last completed run.
          example: Delivered
        state:
          type: string
          description: Machine-readable status of the last completed run.
          example: COMPLETE

    UserAttributeObject:
      type: object
      properties:
        default_value:
          $ref: '#/components/schemas/UserAttributeDefaultValue'
        description:
          $ref: '#/components/schemas/UserAttributeDescription'
        id:
          $ref: '#/components/schemas/UserAttributeId'
        label:
          $ref: '#/components/schemas/UserAttributeLabel'
        multiple_values:
          $ref: '#/components/schemas/UserAttributeMultipleValues'
        name:
          $ref: '#/components/schemas/UserAttributeName'
        system:
          $ref: '#/components/schemas/UserAttributeSystem'
        type:
          $ref: '#/components/schemas/UserAttributeType'

    UserAttributeDefaultValue:
      description: |
        Default value applied when no user-specific value is set. A single value when `multiple_values` is `false`, an array when it is `true`. 
        
        Strings are trimmed. Number attributes accept numbers or numeric strings and store them as strings. Numbers are converted to strings on receipt; send anything beyond the JSON safe integer range (`2^53 - 1`) as a string or it loses precision before Omni sees it. Null or a string that trims to empty means no default. 
        
        An empty array clears the default of a multi-valued attribute and is rejected for a single-valued attribute. A list member that trims to empty is rejected.
      anyOf:
        - type: string
        - type: number
          minimum: -1.7976931348623157e+308
          maximum: 1.7976931348623157e+308
        - type: array
          items:
            anyOf:
              - type: string
              - type: number
                minimum: -1.7976931348623157e+308
                maximum: 1.7976931348623157e+308
        - type: 'null'
      example: us-east

    UserAttributeDescription:
      type: string
      description: |
        Human-readable description of the attribute and its purpose. An empty string is stored as null.
      example: User region for row-level security filtering

    UserAttributeId:
      type: string
      description: Unique identifier for custom attributes. Empty string for system-defined attributes.
      example: a1b2c3d4-e5f6-7890-abcd-ef1234567890

    UserAttributeLabel:
      type: string
      minLength: 1
      description: |
        Display name shown in the Omni UI. Unique among the custom attributes in the organization, and may match the label of a system attribute. 
        
        Must not start with `Omni` and must not be one of the reserved connection labels: `Host`, `Password`, `Database`, `Port`, `Schema`, `Connection Name`.
      example: Region

    UserAttributeMultipleValues:
      type: boolean
      default: false
      description: Whether the attribute accepts an array of values.
      example: false

    UserAttributeName:
      type: string
      minLength: 1
      description: |
        Reference name used in model SQL and in embed SSO URL parameters. Unique within the organization. Must start with a letter, contain only letters, numbers and underscores, and must not start with `omni_`.
      example: region

    UserAttributeSystem:
      type: boolean
      description: |
        System-defined attributes (e.g. `omni_user_id`, `omni_user_email`) are built-in and read-only. Custom attributes have a `false` value.
      example: false

    UserAttributeAllowAnyValue:
      type: boolean
      description: Whether the user or group can switch to a value that is not listed. When `true`, `selectable_values` are suggestions.
      example: false

    UserAttributeSelectableValues:
      type: array
      items:
        type: string
      description: |
        The provisioned values. When `allow_any_value` is `true`, up to 200 suggestions drawn from values assigned to users in the organization.
      example:
        - acme
        - globex

    UserAttributeSelectedValues:
      type: array
      items:
        type: string
      description: The active switch, or `null` when the assigned value applies.
      example:
        - globex

    UserAttributeType:
      type: string
      enum:
        - String
        - Number
      description: |
        Data type that determines valid values. `String` attributes accept text, `Number` attributes accept numeric values stored as strings for precision. Immutable after creation.
      example: String

    UserAttributeSwitchValues:
      type: array
      maxItems: 300
      items:
        type: string
        minLength: 1
        maxLength: 512
      description: |
        Values this user or group can switch the attribute to, validated against the attribute type. 
        
        Numbers are sent as strings to preserve precision beyond `2^53`. At most 300 values of up to 512 characters each.
      example:
        - acme
        - globex

    SelectableValuesResponse:
      type: object
      required:
        - allow_any_value
        - user_attribute_id
        - values
      properties:
        user_attribute_id:
          $ref: '#/components/schemas/UserAttributeId'
          description: The user attribute definition this provisioning applies to.
        values:
          type: array
          maxItems: 300
          items:
            type: string
            minLength: 1
            maxLength: 512
          description: |
            Values this user or group can switch the attribute to. Numbers are returned as strings to preserve precision beyond `2^53`.
          example:
            - acme
            - globex
        allow_any_value:
          $ref: '#/components/schemas/UserAttributeAllowAnyValue'

    UserAttributeSelectionsResponse:
      type: object
      required:
        - records
      properties:
        records:
          type: array
          description: |
            Attributes the calling user can switch. Empty when nothing is provisioned for them. An Organization Admin receives every attribute with `allow_any_value: true`.
          items:
            type: object
            required:
              - allow_any_value
              - label
              - multiple_values
              - name
              - selectable_values
              - selected_values
              - user_attribute_id
            properties:
              user_attribute_id:
                $ref: '#/components/schemas/UserAttributeId'
                description: The user attribute definition ID.
              name:
                $ref: '#/components/schemas/UserAttributeName'
              label:
                $ref: '#/components/schemas/UserAttributeLabel'
                description: Display name shown in the Omni UI.
              multiple_values:
                $ref: '#/components/schemas/UserAttributeMultipleValues'
              allow_any_value:
                $ref: '#/components/schemas/UserAttributeAllowAnyValue'
              selectable_values:
                $ref: '#/components/schemas/UserAttributeSelectableValues'
              selected_values:
                $ref: '#/components/schemas/UserAttributeSelectedValues'

    UploadDeleteResponse:
      type: object
      properties:
        success:
          type: boolean
          description: Whether the deletion was successful
      required:
        - success

    SkillUpdateBody:
      type: object
      properties:
        body:
          $ref: '#/components/schemas/SkillBody'
        description:
          $ref: '#/components/schemas/SkillDescription'
        identifier:
          $ref: '#/components/schemas/SkillIdentifier'
        name:
          $ref: '#/components/schemas/SkillName'
      additionalProperties: false
      minProperties: 1
    
    SkillDeleteResponse:
      type: object
      properties:
        deletedAt:
          type: string
          format: date-time
          description: ISO 8601 timestamp the skill was deleted at
          example: '2026-02-01T09:30:00.000Z'
        id:
          type: string
          format: uuid
          description: ID of the deleted skill
      required:
        - deletedAt
        - id

    SkillCreateBody:
      type: object
      properties:
        body:
          $ref: '#/components/schemas/SkillBody'
        description:
          $ref: '#/components/schemas/SkillDescription'
        identifier:
          $ref: '#/components/schemas/SkillIdentifier'
        name:
          $ref: '#/components/schemas/SkillName'
      required:
        - body
        - description
        - identifier
        - name
      additionalProperties: false
    
    Skill:
      type: object
      properties:
        createdAt:
          $ref: '#/components/schemas/SkillCreatedAt'
        creator:
          $ref: '#/components/schemas/SkillCreator'
        description:
          $ref: '#/components/schemas/SkillDescription'
        id:
          $ref: '#/components/schemas/SkillId'
        identifier:
          $ref: '#/components/schemas/SkillIdentifier'
        name:
          $ref: '#/components/schemas/SkillName'
        updatedAt:
          $ref: '#/components/schemas/SkillUpdatedAt'
        body:
          $ref: '#/components/schemas/SkillBody'
      required:
        - createdAt
        - creator
        - description
        - id
        - identifier
        - name
        - updatedAt
        - body
    
    SkillBody:
      type: string
      minLength: 1
      maxLength: 20000
      description: The instructions injected into the agent's context when the skill runs.
      example: Pull revenue by month for the last four quarters, then...

    SkillDescription:
      type: string
      minLength: 1
      maxLength: 1024
      description: What the skill is for. Shown in the skills list, and given to the agent when it chooses between skills.
      example: Summarizes the quarter and flags anything off trend.

    SkillCreatedAt:
      type: string
      format: date-time
      description: ISO 8601 timestamp of when the skill was created
      example: '2026-01-15T10:00:00.000Z'

    SkillId:
      type: string
      format: uuid
      description: Unique identifier for the skill
      example: a1b2c3d4-e5f6-7890-abcd-ef1234567890

    SkillIdentifier:
      type: string
      minLength: 1
      maxLength: 48
      pattern: ^[a-z0-9][a-z0-9-]*$
      description: Slash-command handle, stored without the slash. Lowercase letters, numbers and hyphens, starting with a letter or number.
      example: quarterly-report

    SkillName:
      type: string
      minLength: 1
      maxLength: 256
      description: A human-readable name for the skill.
      example: Quarterly report
    
    SkillUpdatedAt:
      type: string
      format: date-time
      description: ISO 8601 timestamp of the last time the skill was updated.
      example: '2026-02-01T09:30:00.000Z'
    
    SkillCreator:
      type: object
      description: Details about the user that created the skill.
      properties:
        id:
          type: string
          nullable: true
          format: uuid
          description: |
            Membership ID of the user that created the skill. Null when the creator has no membership or pending invitation in the organization.
          example: b2c3d4e5-f6a7-8901-bcde-f12345678901
        name:
          type: string
          description: Display name of the user that created the skill.
          example: Blob Ross
      required:
        - id
        - name

    TopicObject:
      type: object
      description: A regular (non-composite) topic.
      properties:
        name:
          $ref: '#/components/schemas/TopicName'
        description:
          $ref: '#/components/schemas/TopicDescription'
        base_view_name:
          $ref: '#/components/schemas/TopicBaseViewName'
        label:
          $ref: '#/components/schemas/TopicLabel'
        default_filters:
          $ref: '#/components/schemas/TopicDefaultFilters'
        hidden:
          $ref: '#/components/schemas/TopicHidden'
        join_via_map:
          type: object
          description: Join path mappings for the topic. Keys are view names, values are arrays of view names representing the join path.
          additionalProperties:
            type: array
            items:
              type: string
        join_via_map_key_order:
          type: array
          items:
            type: string
          description: Order of join path keys
        ignored_props:
          type: array
          items:
            type: string
          description: Properties to ignore
        always_where_filters:
          type: object
          description: |
            Filters that applied to all rows in a query. In the model IDE, this is the [`always_where_filters`](/modeling/topics/parameters/always-where-filters) parameter.
        extension_model_id:
          type: string
          format: uuid
          description: The ID of the extension model
        has_frozen_join_via_map:
          type: boolean
          description: Whether the join via map is frozen
        is_composite:
          $ref: '#/components/schemas/TopicIsComposite'
        relationships:
          type: array
          description: Relationships defined in the topic. The properties in each object will typically mirror the [IDE parameters](/modeling/relationships/parameters) used in a model's `relationships` file.
          items:
            $ref: '#/components/schemas/RelationshipObject'
        ide_file_name:
          type: string
          description: The file name for the topic in the IDE
        views:
          $ref: '#/components/schemas/ViewList'

    CompositeTopicImportModels:
      name: import_models
      type: array
      description: Other models this composite topic imports topics from.
      items:
        type: object
        properties:
          model_ref:
            type: string
            description: The ID of the model the topics are imported from.
          topics:
            type: array
            description: The topics imported from the source model.
            items:
              type: object
              properties:
                topic:
                  type: string
                  description: The name of the topic in the source model.
                alias:
                  type: string
                  description: A local alias for the imported topic.
                ignored:
                  type: boolean
                  description: Whether the imported topic is ignored.

    CompositeTopicTopicList:
      name: topics
      type: array
      items:
        type: string
      description: The names of the two or more topics combined into this composite topic. See [topics](/modeling/composite-topics/parameters/topics).

    CompositeTopicObject:
      type: object
      description: A composite topic, which combines two or more topics into a single queryable topic. See [composite topics](/modeling/composite-topics).
      properties:
        ai_context:
          type: string
          description: Context passed to the Omni Agent.
        always_join_all_topics:
          type: boolean
          description: When `true`, every component topic is included in the join regardless of whether fields are selected from it. Defaults to `false`, which prunes unselected topics for better query performance. See [always_join_all_topics](/modeling/composite-topics/parameters/always-join-all-topics).
        cache_policy:
          type: string
          description: The name of a cache policy defined on the model. When unset, resolves to the shortest `max_cache_age` among the composite's component topics. See [cache_policy](/modeling/composite-topics/parameters/cache-policy).
        default_filters:
          $ref: '#/components/schemas/TopicDefaultFilters'
        description:
          $ref: '#/components/schemas/TopicDescription'
        hidden:
          $ref: '#/components/schemas/TopicHidden'
        import_models:
          $ref: '#/components/schemas/CompositeTopicImportModels'
        is_composite:
          $ref: '#/components/schemas/TopicIsComposite'
        label:
          $ref: '#/components/schemas/TopicLabel'
        name:
          $ref: '#/components/schemas/TopicName'
        relationships:
          type: array
          description: Relationships defined in the topic. The properties in each object will typically mirror the [IDE parameters](/modeling/relationships/parameters) used in a model's `relationships` file.
          items:
            $ref: '#/components/schemas/RelationshipObject'
        sample_queries:
          type: object
          description: Modeler-curated example queries for the composite topic. See [sample_queries](/modeling/composite-topics/parameters/sample-queries).
        shared_dimensions:
          type: object
          description: Cross-topic join keys that align the included topics, keyed by the shared dimension name. See [shared_dimensions](/modeling/composite-topics/parameters/shared-dimensions).
        shared_views:
          type: array
          items:
            type: string
          description: Dimension tables joinable from every included topic, such as a date spine. See [shared_views](/modeling/composite-topics/parameters/shared-views).
        topics:
          $ref: '#/components/schemas/CompositeTopicTopicList'
        views:
          $ref: '#/components/schemas/ViewList'

    TopicName:
      type: string
      description: The name of the topic
      example: "blob_roadmap"

    TopicDescription:
      type: string
      description: Description of the topic, shown in the topic picker
      example: "Details about the Blobs R Us product roadmap"

    TopicGroupLabel:
      type: string
      description: Group label for the topic
      example: "Product"

    TopicLabel:
      type: string
      description: The display label for the topic
      example: "Blob Roadmap"

    TopicHidden:
      type: boolean
      description: Whether the topic is hidden from the topic picker.
      example: false

    TopicBaseViewName:
      name: base_view_name
      type: string
      description: Name of the topic's base view
      example: "product_features"

    TopicDefaultFilters:
      name: default_filters
      type: object
      description: Filters applied to every query that starts on the topic.

    TopicIsComposite:
      name: is_composite
      type: boolean
      enum:
        - false
      description: Whether the topic is a composite topic. Absent or `false` for a regular topic; only present and `true` on a [composite topic](/modeling/composite-topics).

    RelationshipObject:
      type: object
      properties:
        left_view_name:
          type: string
          description: The left view in the relationship
        right_view_name:
          type: string
          description: The right view in the relationship
        join_type:
          type: string
          description: The type of join (e.g., ALWAYS_LEFT)
        on:
          type: object
          description: The join condition expression
          properties:
            type:
              type: string
            operator:
              type: string
            operands:
              type: array
              items:
                type: object
            distinct:
              type: boolean
        sql:
          type: string
          description: The SQL expression for the join
        id:
          type: string
          description: Unique identifier for the relationship
        type:
          type: string
          description: The relationship type (e.g., `ONE_TO_MANY`)
        ignored:
          type: boolean
          description: Whether the relationship is ignored
        bidirectional:
          type: boolean
          description: Whether the relationship is bidirectional
        original_on_sql:
          type: string
          description: The original SQL for the join condition
        yaml_path_prefix:
          type: string
          description: The YAML path prefix for the relationship

    ViewList:
      type: array
      description: A list of views included in the topic. The properties in each object will mirror the [IDE parameters](/modeling/views/parameters) available for views.
      items:
        $ref: '#/components/schemas/ViewObject'

    ViewObject:
      type: object
      properties:
        name:
          type: string
          description: The name of the view
        label:
          type: string
          description: The display label for the view
        table_name:
          type: string
          description: The underlying database table name
        schema:
          type: string
          description: The database schema
        schema_label:
          type: string
          description: The display label for the schema
        extension_model_id:
          type: string
          format: uuid
        ide_file_name:
          type: string
          description: The name of the IDE file for the view
        yaml_path:
          type: string
          description: The YAML file path for the view
        filter_only_fields:
          type: array
          items:
            type: string
        is_pseudo_display_view:
          type: boolean
        primary_key:
          type: array
          description: Primary key fields for the view
          items:
            type: object
            properties:
              type:
                type: string
              field_name:
                type: string
        dimensions:
          $ref: '#/components/schemas/DimensionList'
        measures:
          $ref: '#/components/schemas/MeasureList'
                      
    DimensionsList:
      name: dimensions
      type: array
      description: A list of dimensions defined in the view. The properties in each object will mirror the [IDE parameters](/modeling/dimensions/parameters) for `dimensions.`
      items:
        $ref: '#/components/schemas/DimensionObject'

    DimensionObject:
      type: object
      properties:
        ai_context:
          type: string
          description: Context hints for AI usage
        field_name:
          type: string
        view_name:
          type: string
        data_type:
          type: string
          description: Data type (e.g., STRING, NUMBER, TIMESTAMP)
        format:
          type: string
          description: Display format (e.g., ID)
        label:
          type: string
          description: Display label for the dimension
        description:
          type: string
          description: Description of the dimension
        view_label:
          type: string
        extension_model_id:
          type: string
          format: uuid
        is_dimension:
          type: boolean
        fully_qualified_name:
          type: string
        time_frames:
          type: array
          description: Available time frames for date dimensions
          items:
            type: string
        date_type:
          type: string
          description: Date granularity (e.g., DATE, WEEK, MONTH, QUARTER, YEAR)
        drill_fields:
          type: array
          description: Fields available for drilling
          items:
            type: string
        parent_field:
          type: string
          description: Parent field name for grouped dimensions
        parent_label:
          type: string
          description: Parent field label
        group_label:
          type: string
          description: Group label for the dimension
        is_group_parent_field:
          type: boolean
          description: Whether this is a parent field in a group
        data_type_metadata:
          type: object
          description: Additional metadata about the data type
          properties:
            is_date_type:
              type: boolean

    MeasureList:
      name: measures
      type: array
      description: Measures defined in the view. The properties in each object will mirror the [IDE parameters](/modeling/measures/parameters) for `measures.`
      items:
        $ref: '#/components/schemas/MeasureObject'

    MeasureObject:
      type: object
      properties:
        type:
          type: string
          description: Measure type (e.g., aggregation)
        field_name:
          type: string
        view_name:
          type: string
        aggregate_type:
          type: string
          description: Aggregation type (e.g., COUNT, SUM, AVG)
        data_type:
          type: string
        label:
          type: string
        format:
          type: string
        view_label:
          type: string
        ignored:
          type: boolean
          description: Whether the measure is ignored
        display_sql:
          type: string
          description: SQL displayed in the UI
        dialect_sql:
          type: string
          description: SQL specific to the database dialect
        fully_qualified_name:
          type: string
        filters:
          type: object
          description: Filters applied to the measure

    QueryStreamJobLine:
      type: object
      required:
        - job_id
        - status
      description: |
        Per-job line emitted as each job reaches a terminal state. A completed job includes the result set as base64-encoded Arrow IPC in `result`; a failed job carries `error_type` and `error_message` instead.
      properties:
        cache_metadata:
          description: |
            Cache metadata for the result, including row count, byte size, freshness timestamps, requery plan key.
        client_result_id:
          type: string
          description: Client-supplied result ID echoed back for correlating jobs to queries.
        column_name_mapping:
          type: object
          additionalProperties:
            type: string
        error:
          description: Structured error details, e.g. an OAuth re-authentication requirement.
        error_message:
          type: string
          description: Human-readable error message. Present on failed jobs.
          example: No such view "order_items"
        error_type:
          type: string
          description: |
            Machine-readable error category (e.g. `PLAN`, `SQL`). Present on failed jobs.
          example: PLAN
        job_id:
          type: string
          description: ID of the query job this line reports on.
        kill_reason:
          type: string
          description: Why the job was terminiated, when it was cancelled.
        query:
          description: The query that was executed.
        requery_fallback_sql:
          type: string
        requery_sql:
          type: string
          description: SQL to re-query the cached result set, when the result supports requery.
        requery_table_name:
          type: string
        result:
          type: string
          description: |
            Result rows as a base64-encoded Arrow IPC stream. Present on completed jobs. Decode with any Arrow IPC reader and use `summary.fields` to interpret the columns.
        status:
          type: string
          description: |
            Job status. Values include `COMPLETE`, `ERROR`, `FAILED`, and `MISSING`.
          example: COMPLETE
        stream_stats:
          type: object  # QueryStreamStats
          additionalProperties: {}
          description: |
            Streaming stats for a completed job, including the byte window its `result` covers. A large result can be read in pieces with the [Read query results by offset](/api/queries/read-query-results-by-offset) endpoint: decode each `result` and append the bytes before parsing the Arrow stream.
          properties:
            bytes:
              type: integer
              description: Raw result bytes in this response (the decoded length of `result`).
            next_offset:
              oneOf:
                - type: integer
                - type: 'null'
              description: |
                The offset to pass to the results endpoint for the next read, or `null` once this response reached the end of the result.
            offset:
              type: integer
              description: |
                Raw result byte this response starts at: `0`, except on a results read with an offset.
            server_stream:
              type: number
              description: Milliseconds the server spent streaming the result.
            total_bytes:
              type: integer
              description: Size of the whole stored result, in raw bytes.
        summary:
          description: |
            Execution summary. `summary.fields` maps field names to their metadata and is needed to interpret the decoded Arrow table; also carries the generated SQL and cache type.
        used_keys:
          type: object
          additionalProperties:
            type: string
    
    QueryStreamFooterLine:
      type: object
      required:
        - remaining_job_ids
        - timed_out
      description: Last line of the stream.
      properties:
        remaining_job_ids:
          type: array
          items:
            type: string
          description: |
            Job IDs that had not completed when the wait window elapsed. Poll the [Wait for query results](/api/queries/wait-for-query-results) endpoint with these IDs until the list is empty.
          example: []
        timed_out:
          type: string
          enum:
            - 'false'
            - 'true'
          description: |
            Whether the wait window elapsed before every job completed. This is a string value (ex: `"true"`), not a boolean (`true`).
          example: 'false'

  responses:
    ApiError405:
      description: Method Not Allowed
      content:
        application/json:
          schema:
            type: object
            required:
              - detail
              - status
            properties:
              detail:
                type: string
                description: Human-readable error message describing what went wrong.
                example: "Method Not Allowed"
              status:
                type: integer
                description: HTTP status code of the error.
                example: 405

    ApiError422:
      description: Unprocessable content
      content:
        application/json:
          schema:
            type: object
            properties:
              detail:
                type: string
                description: Human-readable error message describing what went wrong.
                example: "Unprocessable content"
              status:
                type: integer
                description: HTTP status code of the error.
                example: 422
            required:
              - detail
              - status

    AiCreditShutoffError:
      description: Composing or verifying the delivery condition requires AI, and the organization is over its AI credit limit. The body carries the stable reason code `shutoff`.
      content:
        application/json:
          schema:
            type: object
            required:
              - code
              - detail
              - status
            properties:
              code:
                type: string
                enum:
                  - shutoff
                description: Stable reason code identifying an AI-credit shutoff.
                example: shutoff
              detail:
                type: string
                description: Human-readable error message describing what went wrong.
                example: The AI credit limit has been reached. Contact your administrator for assistance.
              status:
                type: integer
                description: HTTP status code of the error.
                example: 402

    AiRequestedLlmError: # ApiErrorRequestedLlm422
      description: |
        `requestedLlm` names an LLM the user may not seelct, a thinking level it does not honor, or comes from an embedded user. Use [List available LLMs](/api/ai/list-available-llms) to retrieve a list of available LLMs.
      content:
        application/json:
          schema:
            type: object
            properties:
              detail:
                type: string
                description: Human-readable error message describing what went wrong.
                example: |
                  gpt-6-sol on openai is not an approved LLM for this organization. GET /api/v1/ai/llms (the listLlms tool over MCP) lists the LLMs you can pick; omit requestedLlm to run on the organization default.
              status:
                type: integer
                description: HTTP status code of the error.
                example: 422
            required:
              - detail
              - status

    BadRequest:
      description: Bad Request - Invalid parameters or request body
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    Forbidden:
      description: Forbidden - Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    NotFound:
      description: Not Found - Resource does not exist
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    TooManyRequests:
      description: Too Many Requests - Rate limit exceeded (60 requests/minute)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    MethodNotAllowed:
      description: Method Not Allowed - Invalid HTTP method for this endpoint
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    InternalServerError:
      description: Internal Server Error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    QueryTimeoutError:
      description: Timeout
      content:
        application/json:
          schema:
            type: object
            properties:
              detail:
                type: string
                description: Error message indicating the request timed out
                example: "Request timeout"
              remaining_job_ids:
                type: array
                items:
                  type: string
                  format: uuid
                description: IDs of jobs still processing when the request timed out. Poll the [Wait for query results endpoint](/api/queries/wait-for-query-results) with these IDs to retrieve results.
              timed_out:
                type: boolean
                description: Indicates that the request timed out. Will be `true` for 408 responses.
    
    RoutineConditionApiError400:
      description: |
        Invalid request body, recipient configuration, schedule, or timezone. Also returned when:

        - The destination type is disabled by the organization''s delivery destination settings
        - The schedule is more frequent than the organization allows
        - A Slack destination supplies both or neither of `recipients` and the deprecated `recipientId`, or more than one recipient
        - `destination.userGroupIds` names an unknown ID or a full user group UUID
        - Omni could not author or verify a working condition query.

        Condition failures include a stable `code`. Nothing is created.
      content:
        application/json:
          schema:
            type: object
            properties:
              detail:
                type: string
                description: Human-readable error message describing what went wrong.
                example: The delivery condition is too vague or subjective to evaluate. State a specific, measurable condition, then try again.
              status:
                type: integer
                description: HTTP status code of the error.
                example: 400
              code:
                type: string
                enum:
                  - not_expressible
                  - no_query
                  - malformed_query
                  - dry_run_failed
                description: A machine-readable error code returned when Omni cannot create, replace, or validate a delivery condition. This field is absent for other validation errors.
                example: not_expressible
            required:
              - detail
              - status

    RoutineConditionApiError503:
      description: |
        Omni was temporarily unable to compose or verify the delivery condition. Retryable.
      content:
        application/json:
          schema:
            type: object
            properties:
              detail:
                type: string
                description: Human-readable error message describing what went wrong.
                example: Omni was temporarily unable to compose or verify the delivery condition. Try again shortly.
              status:
                type: integer
                description: HTTP status code of the error.
                example: 503
            required:
              - detail
              - status
