TextQL Sandcastles API

Manage Python sandbox environments for code execution

Operations 14

GET /v2/sandcastles List Sandcastles #
POST /v2/sandcastles Start Sandcastle #
GET /v2/sandcastles/{id} Get Sandcastle Status #
DELETE /v2/sandcastles/{id} Stop Sandcastle #
GET /v2/sandcastles/{id}/executions List Executions #
POST /v2/sandcastles/{id}/execute Execute Code #
POST /v2/sandcastles/{id}/query Load Connector Data #
GET /v2/sandcastles/{id}/files List Files #
POST /v2/sandcastles/{id}/files Upload File #
GET /v2/sandcastles/{id}/files/{path} Download File #
DELETE /v2/sandcastles/{id}/files/{path} Delete File #
POST /v2/sandcastles/{id}/exec Exec Command #
GET /v2/sandcastles/{id}/ontology/diff Diff Ontology (dry run) #
POST /v2/sandcastles/{id}/ontology/changes Create Ontology Change (writeback) #

Documentation

Specifications

Other Resources

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/textql-sandcastles-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

textql-sandcastles-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: TextQL v2 Sandcastles API
  version: '2.0'
  description: REST API for TextQL platform operations. All endpoints require Bearer token authentication.
servers:
- url: https://app.textql.com
security:
- bearerAuth: []
tags:
- name: Sandcastles
  description: Manage Python sandbox environments for code execution
paths:
  /v2/sandcastles:
    get:
      tags:
      - Sandcastles
      summary: List Sandcastles
      description: 'List sandboxes for the authenticated organization with cursor-based

        pagination.


        Each item reports a `status`:

        - `running` — a live worker record was seen recently. Liveness is

        eventually consistent: a sandbox that died abruptly may continue to

        report `running` for a short window (up to ~1 hour).

        - `stale` — the lease is open but no live worker record exists; the

        worker is likely gone. Call `DELETE /v2/sandcastles/{id}` (Stop Sandbox)

        to clear it.

        - `unknown` — liveness could not be determined (cache unavailable); the

        lease is open.

        - `stopped` — the sandbox has been released.


        `GET /v2/sandcastles/{id}` is the authoritative live check for a single

        sandbox.'
      operationId: v2.listSandboxes
      parameters:
      - name: status
        in: query
        schema:
          type: string
          enum:
          - running
          - stopped
          - all
          default: running
        description: 'Filter by lease state. `running` (default) returns sandboxes with an

          open lease — individual items may report `running`, `stale`, or

          `unknown`. `stopped` returns released sandboxes. `all` returns both.

          '
      - name: limit
        in: query
        schema:
          type: integer
          format: int32
          minimum: 1
          maximum: 200
          default: 50
        description: Maximum number of sandboxes to return (default 50, max 200)
      - name: cursor
        in: query
        schema:
          type: string
        description: 'Opaque pagination cursor from a previous response''s `next_cursor`.

          Omit to start from the first page.

          '
      responses:
        '200':
          description: Paginated list of sandboxes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListSandboxesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags:
      - Sandcastles
      summary: Start Sandcastle
      description: Start a Python sandcastle for code execution. Omit the body to create a new sandcastle, or pass back a sandbox_id from create/list to restart that one.
      operationId: v2.startSandbox
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                sandbox_id:
                  type: string
                  description: Restart an existing sandcastle by passing back the sandbox_id returned by create or list; omit to create a new sandcastle. You can only restart your own sandcastles.
      responses:
        '201':
          description: Started sandbox
          content:
            application/json:
              schema:
                type: object
                properties:
                  sandbox_id:
                    type: string
                    description: Unique sandbox identifier
                  created_at:
                    type: string
                    format: date-time
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: The sandbox_id to restart is malformed or does not belong to your organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v2/sandcastles/{id}:
    get:
      tags:
      - Sandcastles
      summary: Get Sandcastle Status
      description: Get the current status of a sandbox, including memory usage and loaded dataframes.
      operationId: v2.getSandboxStatus
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      responses:
        '200':
          description: Sandbox status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SandboxStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags:
      - Sandcastles
      summary: Stop Sandcastle
      description: Stop and destroy a running sandbox.
      operationId: v2.stopSandbox
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      responses:
        '200':
          description: Stop confirmation
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v2/sandcastles/{id}/executions:
    get:
      tags:
      - Sandcastles
      summary: List Executions
      description: 'List a sandbox''s recorded executions (Python, bash, SQL, TQL),

        newest first, with cursor-based pagination.


        Every execution run in a sandbox — via Execute Code, Load Connector

        Data, or chat — is recorded here. Readable even after the sandbox is

        stopped, so it doubles as a post-mortem audit trail.


        Retention: records are kept for **30 days**, then purged. `input` is

        stored in full; `output_preview` is truncated (large output ends with

        a `…[truncated]` marker) and never includes generated files or

        dataframes.'
      operationId: v2.listSandboxExecutions
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      - name: limit
        in: query
        schema:
          type: integer
          format: int32
          minimum: 1
          maximum: 200
          default: 50
        description: Maximum number of executions to return (default 50, max 200)
      - name: cursor
        in: query
        schema:
          type: string
        description: 'Opaque pagination cursor from a previous response''s `next_cursor`.

          Omit to start from the first (newest) page.

          '
      responses:
        '200':
          description: Paginated list of executions, newest first
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListSandboxExecutionsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v2/sandcastles/{id}/execute:
    post:
      tags:
      - Sandcastles
      summary: Execute Code
      description: 'Execute Python code in a sandbox. Returns stdout/stderr output,

        generated files, and dataframe info.


        Executions are recorded — see

        GET /v2/sandcastles/{id}/executions.'
      operationId: v2.executeCode
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - code
              properties:
                code:
                  type: string
                  description: Python code to execute
      responses:
        '200':
          description: Execution result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExecuteCodeResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v2/sandcastles/{id}/query:
    post:
      tags:
      - Sandcastles
      summary: Load Connector Data
      description: 'Load data from a connector into a sandbox dataframe. Provide **exactly

        one** of `query` (an inline SQL query) or `tql_path` (the Ontology

        path of a saved `.tql` file) — sending both or neither returns

        `400 invalid_request`.


        `tql_path` runs **saved Ontology `.tql` files** that the

        caller''s roles can already see — it does not compile caller-authored

        TQL. A path that is not visible to the member''s roles returns

        `404 not_found`, indistinguishable from a path that does not exist. A

        path that does not end in `.tql`, or a file that fails to render,

        returns `400` with the renderer''s message included.


        The response shape is the same for both branches.


        Executions are recorded — see

        GET /v2/sandcastles/{id}/executions.'
      operationId: v2.loadConnectorData
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoadConnectorDataRequest'
      responses:
        '200':
          description: Query result loaded into dataframe
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoadConnectorDataResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v2/sandcastles/{id}/files:
    get:
      tags:
      - Sandcastles
      summary: List Files
      description: 'List files in the sandbox working directory (non-recursive). Pass

        `?path=` to list a subdirectory. Hidden dotfiles (including internal

        sandbox state) are omitted. The `library/` subtree is the mounted

        Ontology: it lists the sandbox''s own copy when that has

        content, otherwise the org library pruned to the caller''s role

        permissions.'
      operationId: v2.listFiles
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      - name: path
        in: query
        required: false
        schema:
          type: string
        description: Relative subdirectory to list; defaults to the working directory root.
      responses:
        '200':
          description: Directory listing
          content:
            application/json:
              schema:
                type: object
                properties:
                  files:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        size_bytes:
                          type: integer
                        is_dir:
                          type: boolean
                        modified_at:
                          type: string
                          format: date-time
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      tags:
      - Sandcastles
      summary: Upload File
      description: Upload a file to a sandbox environment.
      operationId: v2.uploadFile
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
              - file
              properties:
                file:
                  type: string
                  format: binary
                  description: File to upload
      responses:
        '200':
          description: Upload confirmation
          content:
            application/json:
              schema:
                type: object
                properties:
                  filename:
                    type: string
                    description: Basename of the uploaded file
                  size_bytes:
                    type: integer
                    description: Size of the uploaded file in bytes
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v2/sandcastles/{id}/files/{path}:
    get:
      tags:
      - Sandcastles
      summary: Download File
      description: 'Stream the bytes of a single file from the sandbox working directory.

        `path` is the file''s path within the sandbox (subdirectories allowed,

        slashes permitted). Traversal outside the working directory and hidden

        dotfiles are rejected. `library/…` paths serve the sandbox''s own copy

        when present, otherwise the org library pruned to the caller''s role

        permissions (OWNERS files are never served).'
      operationId: v2.downloadFile
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      - $ref: '#/components/parameters/SandboxFilePath'
      responses:
        '200':
          description: File contents
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags:
      - Sandcastles
      summary: Delete File
      description: 'Remove a single file from the sandbox working directory. `path` is the

        file''s path within the sandbox. Directories are rejected; the S3 copy

        (if any) is not affected.'
      operationId: v2.deleteFile
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      - $ref: '#/components/parameters/SandboxFilePath'
      responses:
        '200':
          description: Delete confirmation
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v2/sandcastles/{id}/exec:
    post:
      tags:
      - Sandcastles
      summary: Exec Command
      description: 'Run a one-shot command in the sandbox as a fresh process (via the

        worker shell). Unlike Execute Code,

        which feeds Python into a persistent kernel, `/exec` shares no

        interpreter state between calls — state carries only via the

        filesystem — and returns raw stdout/stderr plus the exit code.


        `kind` selects the interpreter: `bash` (default) runs the command in a

        shell; `python` runs it as a one-shot Python program (no kernel state,

        no dataframe/plot capture). A non-zero exit code is returned in the

        body, not as an HTTP error.


        Executions are recorded — see

        GET /v2/sandcastles/{id}/executions.'
      operationId: v2.exec
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - command
              properties:
                command:
                  type: string
                  description: The command (bash) or program source (python) to run.
                kind:
                  type: string
                  enum:
                  - bash
                  - python
                  default: bash
                  description: Interpreter to run the command with.
                env:
                  type: object
                  additionalProperties:
                    type: string
                  description: Extra environment variables for the process.
      responses:
        '200':
          description: Command result
          content:
            application/json:
              schema:
                type: object
                properties:
                  stdout:
                    type: string
                  stderr:
                    type: string
                  exit_code:
                    type: integer
                  error:
                    type: string
                    description: Worker-level error (e.g. timeout); empty on normal completion.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v2/sandcastles/{id}/ontology/diff:
    get:
      tags:
      - Sandcastles
      summary: Diff Ontology (dry run)
      description: 'Report the pending changes in the sandbox''s Ontology mount

        (`/sandbox/files/library`) relative to its baseline snapshot —

        **without** authoring a change. Use this to decide whether there is

        anything to write back before calling

        Create Ontology Change.


        The diff is scoped to the caller''s `OWNERS` permissions: the mount the

        session sees was pruned at materialization, so only permitted paths can

        appear here.'
      operationId: v2.ontologyDiff
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      responses:
        '200':
          description: Pending library changes
          content:
            application/json:
              schema:
                type: object
                properties:
                  has_changes:
                    type: boolean
                    description: True when the library differs from its baseline snapshot.
                  diffs:
                    type: array
                    items:
                      $ref: '#/components/schemas/LibraryChangeDiff'
                  raw_diff:
                    type: string
                    description: The unified git diff, normalized to library-relative paths.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v2/sandcastles/{id}/ontology/changes:
    post:
      tags:
      - Sandcastles
      summary: Create Ontology Change (writeback)
      description: 'Persist the sandbox''s edits to `/sandbox/files/library` back to the

        organization''s **Ontology** by authoring a reviewable change. This

        is the API equivalent of the in-product writeback tool: it diffs the

        session library against its baseline snapshot, commits the delta to a

        change ref, detects merge conflicts, and submits the change for review.


        **Stage first.** A change can only be created from real file changes —

        edit files under `/sandbox/files/library` (via

        Exec or

        Upload) **before** calling this.

        Creating a new change with no changes returns `400`.


        **Review & permissions.** The change is submitted `OPEN` for admin review

        (or `DRAFT` when `draft` is true). Every changed path is revalidated

        against the caller''s `OWNERS` permissions at merge — a change cannot widen

        access. If an auto-approve rule matches, the response status is

        `APPROVED` and the change is already live.


        **Updates & conflicts.** Pass `change_number` to file a new revision of an

        existing open/draft change (title/description optional — inherited if

        omitted). If the library has drifted, the response has

        `has_conflicts: true`, the session''s `library/` is re-materialized with

        `.rej` markers, and the change stays `RESERVED` until you resolve the

        conflicts and re-submit with the same `change_number`.'
      operationId: v2.createOntologyChange
      parameters:
      - $ref: '#/components/parameters/SandboxId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  maxLength: 50
                  description: Short summary (≤50 chars). Required for a new change.
                description:
                  type: string
                  description: Markdown explanation of the changes. Required for a new change.
                draft:
                  type: boolean
                  default: false
                  description: File as DRAFT instead of OPEN (not yet ready for review).
                change_number:
                  type: integer
                  format: int32
                  description: Set to revise an existing change (creates a new changeset).
      responses:
        '201':
          description: Change created or updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  change_id:
                    type: string
                  change_number:
                    type: integer
                    format: int32
                  status:
                    type: string
                    enum:
                    - open
                    - draft
                    - approved
                    - reserved
                    description: Change lifecycle state after submission.
                  git_ref:
                    type: string
                  has_conflicts:
                    type: boolean
                  conflicts:
                    type: string
                    description: Human-readable conflict view (present when has_conflicts).
                  auto_approved:
                    type: boolean
                  diffs:
                    type: array
                    items:
                      $ref: '#/components/schemas/LibraryChangeDiff'
                  raw_diff:
                    type: string
        '400':
          description: No changes to write back, or invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Referenced change is approved/denied and cannot be updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    SandboxStatus:
      type: object
      properties:
        status:
          type: string
          description: Point-in-time liveness for this sandbox. `stale` and `unknown` are list-only states (see SandboxSummary); this endpoint reports only `running` or `stopped`.
          enum:
          - running
          - stopped
        memory_usage:
          type: string
          description: Human-readable memory usage (only when running)
        dataframes:
          type: array
          description: Loaded dataframes (only when running)
          items:
            $ref: '#/components/schemas/DataframeInfo'
    LoadConnectorDataRequest:
      type: object
      required:
      - connector_id
      properties:
        connector_id:
          type: integer
          format: int32
          description: Connector to query
        query:
          type: string
          description: 'Inline SQL query to execute. Mutually exclusive with `tql_path` —

            provide exactly one of the two (`400 invalid_request` otherwise).

            '
        tql_path:
          type: string
          description: 'Ontology path of a saved `.tql` file to run (must end in

            `.tql`). Only files already visible to the member''s roles can be

            run — a path that is not visible returns `404 not_found`,

            indistinguishable from a path that does not exist. Mutually

            exclusive with `query` — provide exactly one of the two

            (`400 invalid_request` otherwise).

            '
        params:
          type: object
          description: 'Parameter values passed to the saved `.tql` file when it is

            rendered. Only used with `tql_path`. If rendering fails, the

            request returns `400` with the renderer''s message included.

            '
        max_rows:
          type: integer
          format: int64
          description: 'Maximum number of rows to load. Only applies to the `tql_path`

            branch; clamped to the range 1–2,000,000 (default 2,000,000).

            '
        dataframe_name:
          type: string
          description: 'Name for the resulting dataframe. Defaults to `connector_{id}` for

            `query`, or to the `.tql` filename stem (e.g. `revenue` for

            `reports/revenue.tql`) for `tql_path`.

            '
    LibraryChangeDiff:
      type: object
      description: One changed file in an Ontology change.
      properties:
        name:
          type: string
          description: Display name for the change.
        old_path:
          type: string
        new_path:
          type: string
        additions:
          type: integer
          format: int64
        deletions:
          type: integer
          format: int64
        is_new:
          type: boolean
        is_delete:
          type: boolean
        is_rename:
          type: boolean
        is_binary:
          type: boolean
    LoadConnectorDataResponse:
      type: object
      properties:
        preview:
          type: string
          description: 'Text summary of the loaded data. Format varies by size: for smaller results,

            a dataframe preview string from the sandbox; for larger results (2048+ rows),

            a markdown table of the first 100 rows.

            '
        dataframe_name:
          type: string
        num_rows:
          type: integer
          format: int64
        num_cols:
          type: integer
          format: int64
    SandboxExecution:
      type: object
      properties:
        id:
          type: string
          description: Unique execution identifier
        kind:
          type: string
          enum:
          - python
          - bash
          - sql
          - tql
          description: Execution type
        source:
          type: string
          enum:
          - platform_api
          - chat
          - dashboard
          - playbook
          - internal
          description: Product surface that triggered the execution
        input:
          type: string
          description: 'The executed input — code (python/bash), SQL text (sql), or the

            library path (tql).

            '
        output_preview:
          type: string
          description: 'Truncated output preview (up to 16 KB; longer output ends with a

            `…[truncated]` marker). Does not include generated files or

            dataframes.

            '
        error:
          type: string
          description: Error message when the execution failed; empty otherwise.
        duration_ms:
          type: integer
          format: int64
          description: Execution duration in milliseconds
        created_at:
          type: string
          format: date-time
          description: When the execution ran
    ListSandboxExecutionsResponse:
      type: object
      required:
      - executions
      properties:
        executions:
          type: array
          items:
            $ref: '#/components/schemas/SandboxExecution'
        next_cursor:
          type: string
          description: 'Opaque cursor to pass as `cursor` on the next request. Omitted when

            there are no more results.

            '
    ListSandboxesResponse:
      type: object
      required:
      - sandboxes
      properties:
        sandboxes:
          type: array
          items:
            $ref: '#/components/schemas/SandboxSummary'
        next_cursor:
          type: string
          nullable: true
          description: 'Opaque cursor to pass as `cursor` on the next request. Omitted when

            there are no more results.

            '
    ExecuteCodeResponse:
      type: object
      properties:
        output:
          type: array
          nullable: true
          items:
            type: string
          description: 'Stdout/stderr output as an array of strings, one element per print call.

            Null when execution fails with an error.

            '
        error:
          type: string
          description: 'Execution error message, including the Python exception type and message

            (e.g. "executing Python code: ZeroDivisionError(''division by zero'')").

            Only present when execution fails. When set, output is null.

            '
        execution_time_ms:
          type: integer
          format: int64
          description: Execution duration in milliseconds
        files:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              url:
                type: string
              mime_type:
                type: string
        dataframes:
          type: array
          descripti

# --- truncated at 32 KB (37 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/textql/refs/heads/main/openapi/textql-sandcastles-api-openapi.yml