openapi: 3.1.0
info:
  title: SandBase API
  description: |
    SandBase is an agent-native platform for connecting Agents to the real world.
    Use one account and one key to call Models, ready-made APIs, and Agent Services.

    ## Authentication
    All API requests require an API key passed via `Authorization: Bearer sk-sb-YOUR_KEY` header.

    ## SDK Compatibility
    - **LLM Gateway** (`/v1/chat/completions`): Use OpenAI SDK with `base_url="https://api.sandbase.ai/v1"`
    - **Embeddings** (`/v1/embeddings`): Use OpenAI SDK with `base_url="https://api.sandbase.ai/v1"`
    - **Anthropic Messages** (`/v1/messages`): Use Anthropic SDK with `base_url="https://api.sandbase.ai"`
    - **Agents and Sessions** (`/v1/agents`, `/v1/sessions`): Use Anthropic SDK with `base_url="https://api.sandbase.ai"`
    - **Environments** (`/sandboxes`): Use E2B SDK with `api_url="https://api.sandbase.ai"`
  version: 1.0.0
  x-sandbase-session-contract:
    status: active
    feature: persistent public Session identity
    description: >-
      Public session_id identifies persistent Agent interaction across Direct Session, Service (Endpoint), and Schedule
      (Deployment) entry points. Service invocation creates or continues a Session and sends one event. Every manual or
      scheduled Deployment trigger creates a public drun_* DeploymentRun and attempts to create one new Session.
      Internal Runtime Session identities are never exposed.
  contact:
    name: SandBase
    url: https://www.sandbase.ai
  license:
    name: Proprietary

servers:
  - url: https://api.sandbase.ai
    description: Production

security:
  - BearerAuth: []

tags:
  - name: Chat Completions
    description: OpenAI-compatible LLM Gateway
  - name: Embeddings
    description: OpenAI-compatible text embeddings
  - name: Messages
    description: Anthropic-compatible Messages API
  - name: Agents
    description: Build and manage Agent definitions
  - name: Services
    description: Publish and invoke tested Agents through Endpoint resources
  - name: Runs
    description: Agent run management
  - name: Events
    description: Run event streaming
  - name: Environments
    description: Execution environment configuration
  - name: Sandboxes
    description: Lower-level environment runtime lifecycle
  - name: Models
    description: Model catalog
  - name: Tasks
    description: Asynchronous model task status, settlement, and usage
  - name: Webhooks
    description: Organization-scoped callback registrations

paths:
  # --- LLM Gateway ------------------------------------------------
  /v1/chat/completions:
    post:
      operationId: createChatCompletion
      tags: [Chat Completions]
      summary: Create chat completion
      description: OpenAI-compatible chat completions endpoint for SandBase model APIs.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatCompletionRequest'
      responses:
        '200':
          description: Chat completion response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChatCompletionResponse'
            text/event-stream:
              schema:
                type: string
                description: SSE stream of chat completion chunks

  /v1/embeddings:
    post:
      operationId: createEmbedding
      tags: [Embeddings]
      summary: Create embeddings
      description: OpenAI-compatible embeddings endpoint for semantic search, RAG, clustering, and classification.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmbeddingRequest'
      responses:
        '200':
          description: Embedding response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmbeddingResponse'

  /v1/messages:
    post:
      operationId: createMessage
      tags: [Messages]
      summary: Create message (Anthropic compatible)
      description: Anthropic Messages API compatible endpoint.
      parameters:
        - name: anthropic-version
          in: header
          required: true
          schema:
            type: string
            example: "2023-06-01"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [model, max_tokens, messages]
              properties:
                model:
                  type: string
                  example: "claude-sonnet-4"
                max_tokens:
                  type: integer
                  example: 4096
                messages:
                  type: array
                  items:
                    type: object
                    properties:
                      role:
                        type: string
                        enum: [user, assistant]
                      content:
                        type: string
                stream:
                  type: boolean
                  default: false
      responses:
        '200':
          description: Message response

  # --- Tasks ------------------------------------------------------
  /v1/tasks/{task_id}/cost:
    get:
      operationId: getTaskCost
      tags: [Tasks]
      summary: Get task cost and usage
      description: |
        Returns the settlement status, final user cost, estimated cost, and token usage for a recent task.
        When `settled` is false, `cost` is not final and must not be used for billing or reconciliation.
        Use `estimated_cost` only as a display estimate until final settlement completes.
        Older task execution records may be archived after the retention window and then return 404.
        Use billing records or billing exports for long-term reconciliation.
      parameters:
        - name: task_id
          in: path
          required: true
          description: SandBase task ID returned by POST /v1/run or POST /v1/generations.
          schema:
            type: string
      responses:
        '200':
          description: Task cost and usage
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCostResponse'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCostError'
        '404':
          description: Task does not exist or is not accessible with the current API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCostError'

  # --- Services (Endpoint resources) -----------------------------
  /v1/endpoint_runtime_profiles:
    get:
      operationId: listEndpointRuntimeProfiles
      tags: [Services]
      summary: List enabled Service runtime profiles
      description: Returns the public capabilities of runtimes enabled for Service creation.
      responses:
        '200':
          description: Enabled runtime profiles
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/EndpointRuntimeProfile'

  /v1/endpoints:
    post:
      operationId: createEndpoint
      tags: [Services]
      summary: Create a Service
      description: |
        Creates a Service (Endpoint resource) from either a declarative runtime definition or an existing Agent and Environment.
        The two creation modes are mutually exclusive. Declarative YAML uses the same schema as JSON.
        The bound Agent and Environment must use the same runtime provider, and that provider must be enabled for the organization.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/CreateDeclarativeEndpointRequest'
                - $ref: '#/components/schemas/CreateAdvancedEndpointRequest'
          application/yaml:
            schema:
              $ref: '#/components/schemas/CreateDeclarativeEndpointRequest'
          application/x-yaml:
            schema:
              $ref: '#/components/schemas/CreateDeclarativeEndpointRequest'
      responses:
        '201':
          description: Endpoint created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Endpoint'
        '400':
          description: Invalid definition or creation mode
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
        '422':
          description: Runtime or Skill is incompatible
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
    get:
      operationId: listEndpoints
      tags: [Services]
      summary: List Services
      responses:
        '200':
          description: Service list
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Endpoint'
                  next_page:
                    type: string

  /v1/endpoints/{id}:
    get:
      operationId: getEndpoint
      tags: [Services]
      summary: Get a Service
      parameters:
        - $ref: '#/components/parameters/EndpointId'
      responses:
        '200':
          description: Service details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Endpoint'
        '404':
          description: Service not found
    patch:
      operationId: updateEndpoint
      tags: [Services]
      summary: Update a Service
      description: Declarative definition fields are immutable in this release; operational fields such as name, status, and protocols may be updated.
      parameters:
        - $ref: '#/components/parameters/EndpointId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateEndpointRequest'
      responses:
        '200':
          description: Service updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Endpoint'
        '409':
          description: Declarative Service definition is immutable
    delete:
      operationId: deleteEndpoint
      tags: [Services]
      summary: Delete a Service
      parameters:
        - $ref: '#/components/parameters/EndpointId'
      responses:
        '200':
          description: Service deleted

  /v1/endpoints/{id}/run:
    post:
      operationId: runEndpoint
      tags: [Services]
      summary: Invoke a Service over REST
      description: |
        Creates or continues a persistent Session and sends one standard event. Omit `session_id` to create a
        Session fixed to the Endpoint's Agent version and Runtime Environment snapshot. Provide `session_id` to
        continue that Session. Returns the persistent Session ID and accepted events; no public execution Run or
        internal Runtime Session identity is exposed.
      parameters:
        - $ref: '#/components/parameters/EndpointId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EndpointInvokeRequest'
      responses:
        '202':
          description: Session event accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EndpointInvokeAccepted'
        '502':
          description: Agent runtime or Skill materialization failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
        '422':
          description: Runtime or Sandbox provider is unsupported; no turn was delivered
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
        '503':
          description: Stateful persistence or recovery is unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

  /v1/endpoints/{id}/acp:
    post:
      operationId: callEndpointAcp
      tags: [Services]
      summary: Invoke a Service over experimental ACP
      description: |
        Experimental ACP-over-HTTP JSON-RPC transport supporting initialize, session/new, session/prompt, and
        session/cancel. Prompt output is newline-delimited JSON. Runtime failures use redacted public error messages;
        the remote transport may change while ACP remains experimental.
      parameters:
        - $ref: '#/components/parameters/EndpointId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: ACP JSON-RPC response or newline-delimited prompt stream
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
            application/x-ndjson:
              schema:
                type: string
                description: ACP session/update notifications followed by the final JSON-RPC result.
        '400':
          description: Invalid JSON-RPC request
        '404':
          description: Service or ACP session not found

  # --- Agents -----------------------------------------------------
  /v1/agents:
    post:
      operationId: createAgent
      tags: [Agents]
      summary: Create agent
      description: Create a reusable, versioned agent configuration.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAgentRequest'
      responses:
        '200':
          description: Agent created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
    get:
      operationId: listAgents
      tags: [Agents]
      summary: List agents
      parameters:
        - { name: 'created_at[gte]', in: query, schema: { type: string, format: date-time } }
        - { name: 'created_at[lte]', in: query, schema: { type: string, format: date-time } }
        - { name: include_archived, in: query, schema: { type: boolean, default: false } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 20 } }
        - { name: page, in: query, schema: { type: string } }
      responses:
        '200':
          description: Agent list
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AgentListPage' }

  /v1/agents/{id}:
    get:
      operationId: getAgent
      tags: [Agents]
      summary: Get agent
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - name: version
          in: query
          schema: { type: integer, minimum: 1 }
          description: Return the immutable Agent snapshot for this numeric version. Omit for the current version.
      responses:
        '200':
          description: Agent details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
    post:
      operationId: updateAgent
      tags: [Agents]
      summary: Update agent
      description: Update agent configuration. Creates a new version when changes are made.
      parameters:
        - $ref: '#/components/parameters/AgentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAgentRequest'
      responses:
        '200':
          description: Agent updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'

  /v1/agents/{id}/archive:
    post:
      operationId: archiveAgent
      tags: [Agents]
      summary: Archive agent
      description: Archive an agent, making it read-only.
      parameters:
        - $ref: '#/components/parameters/AgentId'
      responses:
        '200':
          description: Agent archived
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'

  /v1/agents/{id}/versions:
    get:
      operationId: listAgentVersions
      tags: [Agents]
      summary: List agent versions
      parameters:
        - $ref: '#/components/parameters/AgentId'
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 20 } }
        - { name: page, in: query, schema: { type: string } }
      responses:
        '200':
          description: Version history
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Agent'
                  next_page: { type: string, nullable: true }

  # --- Sessions ---------------------------------------------------
  /v1/sessions:
    post:
      operationId: createSession
      tags: [Sessions]
      summary: Create session
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSessionRequest'
      responses:
        '200':
          description: Session created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '422':
          description: The initial_events batch was rejected without persisting Events; Session cleanup follows the documented best-effort compensation path. Tool confirmation, custom tool result, tool result, and define outcome currently return `unsupported_event_type`; any `session_thread_id` returns `unsupported_multiagent`.
          content:
            application/json:
              schema:
                type: object
                required: [error]
                properties:
                  error:
                    type: object
                    required: [type, message]
                    properties:
                      type:
                        type: string
                        enum: [unsupported_event_type, unsupported_multiagent]
                      message: { type: string }
        '502':
          description: Native runtime delivery outcome is unknown. Recover the returned Session and inspect Events before retrying.
          content:
            application/json:
              schema:
                type: object
                required: [error, session_id]
                properties:
                  error:
                    type: object
                    required: [type, message]
                    properties:
                      type: { type: string, enum: [delivery_outcome_unknown] }
                      message: { type: string }
                  session_id:
                    type: string
                    pattern: '^sess_'
                  delivery_status:
                    type: string
                    enum: [unknown]
                  recovery:
                    type: object
                    required: [recoverable, retry_create, action]
                    properties:
                      recoverable: { type: boolean, enum: [true] }
                      retry_create: { type: boolean, enum: [false] }
                      action: { type: string, enum: [get_session_and_list_events] }
    get:
      operationId: listSessions
      tags: [Sessions]
      summary: List sessions
      parameters:
        - name: agent_id
          in: query
          schema:
            type: string
          description: Filter by agent ID
        - name: agent_version
          in: query
          schema: { type: integer, minimum: 1 }
        - name: include_archived
          in: query
          schema: { type: boolean, default: false }
        - name: 'created_at[gt]'
          in: query
          schema: { type: string, format: date-time }
        - name: 'created_at[gte]'
          in: query
          schema: { type: string, format: date-time }
        - name: 'created_at[lt]'
          in: query
          schema: { type: string, format: date-time }
        - name: 'created_at[lte]'
          in: query
          schema: { type: string, format: date-time }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 20 } }
        - { name: page, in: query, schema: { type: string } }
      responses:
        '200':
          description: Session list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Session'
                  next_page: { type: string, nullable: true }
                  prev_page: { type: string, nullable: true }

  /v1/sessions/{id}:
    get:
      operationId: getSession
      tags: [Sessions]
      summary: Get session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
    post:
      operationId: updateSession
      tags: [Sessions]
      summary: Update session title or metadata
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title: { type: string }
                metadata: { type: object }
                agent:
                  $ref: '#/components/schemas/SessionAgentUpdate'
                vault_ids:
                  type: array
                  items: { type: string, pattern: '^vlt_' }
                  description: Reserved for Claude compatibility; rejected until vault attachment is supported.
      responses:
        '200':
          description: Session updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
    delete:
      operationId: deleteSession
      tags: [Sessions]
      summary: Permanently delete a non-running session
      description: Allowed only when the Session is neither running nor rescheduling. Deletion is permanent; use archive to retain history.
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session deleted
  /v1/sessions/{id}/archive:
    post:
      operationId: archiveSession
      tags: [Sessions]
      summary: Archive session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      responses:
        '200':
          description: Session archived
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'

  /v1/sessions/{id}/events:
    post:
      operationId: sendEvents
      tags: [Events]
      summary: Send events to session
      parameters:
        - $ref: '#/components/parameters/SessionId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendEventsRequest'
      responses:
        '200':
          description: Implemented event types accepted. Schema-recognized event types without runtime behavior are never returned as accepted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AcceptedSessionEvents'
        '422':
          description: The batch was rejected without persisting any event. Tool confirmation, custom tool result, tool result, and define outcome currently return `unsupported_event_type`; any `session_thread_id` returns `unsupported_multiagent`.
          content:
            application/json:
              schema:
                type: object
                required: [error]
                properties:
                  error:
                    type: object
                    required: [type, message]
                    properties:
                      type:
                        type: string
                        enum: [unsupported_event_type, unsupported_multiagent]
                      message: { type: string }
    get:
      operationId: listEvents
      tags: [Events]
      summary: List session events
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: types
          in: query
          schema: { type: array, items: { type: string } }
          style: form
          explode: true
        - name: order
          in: query
          schema: { type: string, enum: [asc, desc], default: asc }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 100 }
        - name: page
          in: query
          schema: { type: string }
        - name: 'created_at[gt]'
          in: query
          schema: { type: string, format: date-time }
        - name: 'created_at[gte]'
          in: query
          schema: { type: string, format: date-time }
        - name: 'created_at[lt]'
          in: query
          schema: { type: string, format: date-time }
        - name: 'created_at[lte]'
          in: query
          schema: { type: string, format: date-time }
      responses:
        '200':
          description: Event list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/SessionEvent'

  /v1/sessions/{id}/events/stream:
    get:
      operationId: streamEvents
      tags: [Events]
      summary: Stream session events (SSE)
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - name: cursor
          in: query
          schema: { type: string }
          description: Resume after the last delivered event ID. Last-Event-ID is also accepted.
        - name: event_delta
          in: query
          style: form
          explode: true
          schema:
            type: array
            items: { type: string, enum: [agent.message, agent.thinking] }
          description: Repeatable Claude-compatible request for best-effort event preview deltas.
      responses:
        '200':
          description: SSE event stream
          content:
            text/event-stream:
              schema:
                type: string

  # --- Environments -----------------------------------------------
  /v1/environments:
    post:
      operationId: createEnvironment
      tags: [Environments]
      summary: Create environment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEnvironmentRequest'
      responses:
        '200':
          description: Environment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Environment'
    get:
      operationId: listEnvironments
      tags: [Environments]
      summary: List environments
      parameters:
        - { name: include_archived, in: query, schema: { type: boolean, default: false } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 20 } }
        - { name: page, in: query, schema: { type: string } }
      responses:
        '200':
          description: Environment list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Environment'
                  next_page: { type: string, nullable: true }

  /v1/environments/{id}:
    get:
      operationId: getEnvironment
      tags: [Environments]
      summary: Get environment
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      responses:
        '200':
          description: Environment details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Environment'
    patch:
      operationId: updateEnvironment
      tags: [Environments]
      summary: Update environment metadata, runtime configuration, or credential bindings
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateEnvironmentRequest'
      responses:
        '200':
          description: Environment updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Environment'
    post:
      operationId: updateEnvironmentClaude
      tags: [Environments]
      summary: Update environment
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/UpdateEnvironmentRequest' }
      responses:
        '200':
          description: Environment updated
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Environment' }
    delete:
      operationId: deleteEnvironment
      tags: [Environments]
      summary: Delete environment
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      responses:
        '200':
          description: Environment deleted

  /v1/environments/{id}/archive:
    post:
      operationId: archiveEnvironment
      tags: [Environments]
      summary: Archive environment
      parameters:
        - $ref: '#/components/parameters/EnvironmentId'
      responses:
        '200':
          description: Environment archived
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Environment'

  # --- Sandboxes --------------------------------------------------
  /sandboxes:
    post:
      operationId: createSandbox
      tags: [Sandboxes]
      summary: Create sandbox
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSandboxRequest'
      responses:
        '200':
          description: Sandbox created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Sandbox'

  /sandboxes/{id}:
    get:
      operationId: getSandbox
      tags: [Sandboxes]
      summary: Get sandbox
      parameters:
        - $ref: '#/components/parameters/SandboxId'
      responses:
        '200':
          description: Sandbox details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Sandbox'
    delete:
      operationId: destroySandbox
      tags: [Sandboxes]
      summary: Destroy sandbox
      parameters:
        - $ref: '#/components/parameters/SandboxId'
      responses:
        '204':
          description: Sandbox destroyed

  /sandboxes/{id}/pause:
    post:
      operationId: pauseSandbox
      tags: [Sandboxes]
      summary: Pause sandbox
      parameters:
        - $ref: '#/components/parameters/SandboxId'
      responses:
        '204':
          description: Sandbox paused

  /sandboxes/{id}/connect:
    post:
      operationId: resumeSandbox
      tags: [Sandboxes]
      summary: Resume sandbox
      parameters:
        - $ref: '#/components/parameters/SandboxId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                timeoutMs:
                  type: integer
                  description: New timeout in milliseconds
      responses:
        '200':
          description: Sandbox resumed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Sandbox'

  /sandboxes/{id}/timeout:
    post:
      operationId: setSandboxTimeout
      tags: [Sandboxes]
      summary: Set sandbox timeout
      parameters:
        - $ref: '#/components/parameters/SandboxId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [timeout]
              properties:
                timeout:
                  type: integer
                  description: Timeout in seconds
      responses:
        '204':
          description: Timeout updated

  # --- Models -----------------------------------------------------
  /v1/models:
    get:
      operationId: listModels
      tags: [Models]
      summary: List models
      description: List enabled API models with pagination, filters, and fuzzy search by model name. The list response returns lightweight catalog fields only; detailed pricing is available from the model detail endpoint inside model_card.
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
            minimum: 1
          description: Page number.
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
          description: Number of results per page.
        - name: q
          in: query
          schema:
            type: string
          description: Fuzzy search over model name, display name, vendor, and description.
        - name: vendor
          in: query
          schema:
            type: string
          description: Filter by vendor.
        - name: type
          in: query
          schema:
            type: string
            enum: [llm, vision, image, video, audio, embedding]
          description: Filter by model type.
        - name: order
          in: query
          schema:
            type: string
            enum: [sort_order, most-popular, newest, name]
            default: sort_order
          description: Sort order.
      responses:
        '200':
          description: Model list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelListResponse'

  /v1/models/{id_or_name}:
    get:
      operationId: getModel
      tags: [Models]
      summary: Get model details
      description: Get model details by model ID or full model name. Model names containing slashes are supported, for example /v1/models/openai/gpt-4o.
      parameters:
        - name: id_or_name
          in: path
          required: true
          schema:
            type: string
          description: Model ID or full model name.
      responses:
        '200':
          description: Model details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelDetail'

  /events/webhooks:
    post:
      operationId: createWebhook
      tags: [Webhooks]
      summary: Register a webhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, url]
              properties:
                name: { type: string }
                url: { type: string, format: uri }
                resourceType: { type: string, enum: [sandbox, session, model], default: sandbox }
                events: { type: array, items: { type: string } }
                enabled: { type: boolean, default: true }
                signatureSecret: { type: string, writeOnly: true }
      responses:
        '200':
          description: Webhook registered; the signing secret is returned only on creation
    get:
      operationId: listWebhooks
      tags: [Webhooks]
      summary: List webhook registrations
      responses:
        '200':
          description: Webhook registration list

  /events/webhooks/{id}:
    patch:
      operationId: updateWebhook
      tags: [Webhooks]
      summary: Update a webhook registration
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string }
                url: { type: string, format: uri }
                resourceType: { type: string, enum: [sandbox, session, model] }
                events: { type: array, items: { type: string } }
                enabled: { type: boolean }
      responses:
        '200':
          description: Webhook updated
    delete:
      operationId: deleteWebhook
      tags: [Webhooks]
      summary: Delete a webhook registration
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        '204':
          description: Webhook deleted

  /v1/deployments:
    post:
      operationId: createDeployment
      tags: [Deployments]
      summary: Create deployment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDeploymentRequest'
      responses:
        '200':
          description: Deployment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deployment'
    get:
      operationId: listDeployments
      tags: [Deployments]
      summary: List deployments
      parameters:
        - { name: agent_id, in: query, schema: { type: string } }
        - { name: 'created_at[gte]', in: query, schema: { type: string, format: date-time } }
        - { name: 'created_at[lte]', in: query, schema: { type: string, format: date-time } }
        - { name: include_archived, in: query, schema: { type: boolean, default: false } }
        - { name: status, in: query, schema: { type: string, enum: [active, paused] } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 20 } }
        - { name: page, in: query, schema: { type: string } }
      responses:
        '200':
          description: Deployment list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Deployment'
                  next_page: { type: string, nullable: true }

  /v1/deployments/{id}:
    get:
      operationId: getDeployment
      tags: [Deployments]
      summary: Get deployment
      parameters:
        - $ref: '#/components/parameters/DeploymentId'
      responses:
        '200':
          description: Deployment details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deployment'
    patch:
      operationId: updateDeployment
      tags: [Deployments]
      summary: Update deployment
      parameters:
        - $ref: '#/components/parameters/DeploymentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDeploymentRequest'
      responses:
        '200':
          description: Deployment updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deployment'
    post:
      operationId: updateDeploymentClaude
      tags: [Deployments]
      summary: Update deployment (Claude-compatible alias)
      parameters:
        - $ref: '#/components/parameters/DeploymentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDeploymentRequest'
      responses:
        '200':
          description: Deployment updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deployment'

  /v1/deployments/{id}/run:
    post:
      operationId: runDeployment
      tags: [Deployments]
      summary: Run deployment now (Claude-compatible alias)
      parameters:
        - $ref: '#/components/parameters/DeploymentId'
        - name: Idempotency-Key
          in: header
          required: false
          schema: { type: string }
      responses:
        '200':
          description: Existing or newly created DeploymentRun result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeploymentRun'

  /v1/deployments/{id}/runs:
    post:
      operationId: triggerDeploymentRun
      tags: [Deployments]
      summary: Trigger a DeploymentRun
      description: Creates one durable trigger record and attempts to create exactly one new Session.
      parameters:
        - $ref: '#/components/parameters/DeploymentId'
        - name: Idempotency-Key
          in: header
          required: false
          schema: { type: string }
      responses:
        '200':
          description: Existing or newly created DeploymentRun result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeploymentRun'
        '409':
          description: The same idempotency key was used with a different request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
    get:
      operationId: listDeploymentRuns
      tags: [Deployments]
      summary: List DeploymentRuns, including succeeded and failed records
      parameters:
        - $ref: '#/components/parameters/DeploymentId'
      responses:
        '200':
          description: DeploymentRun list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/DeploymentRun'

  /v1/deployments/{id}/runs/{drun_id}:
    get:
      operationId: getDeploymentRun
      tags: [Deployments]
      summary: Get a DeploymentRun creation result
      parameters:
        - $ref: '#/components/parameters/DeploymentId'
        - name: drun_id
          in: path
          required: true
          schema: { type: string, pattern: '^drun_' }
      responses:
        '200':
          description: DeploymentRun details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeploymentRun'
        '409':
          description: The DeploymentRun is still pending and has not resolved to a Session creation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

  /v1/deployment_runs:
    get:
      operationId: listAllDeploymentRuns
      tags: [Deployments]
      summary: List DeploymentRuns across deployments
      parameters:
        - name: deployment_id
          in: query
          schema: { type: string }
        - name: agent_id
          in: query
          schema: { type: string }
        - { name: 'created_at[gt]', in: query, schema: { type: string, format: date-time } }
        - { name: 'created_at[gte]', in: query, schema: { type: string, format: date-time } }
        - { name: 'created_at[lt]', in: query, schema: { type: string, format: date-time } }
        - { name: 'created_at[lte]', in: query, schema: { type: string, format: date-time } }
        - { name: has_error, in: query, schema: { type: boolean } }
        - { name: trigger_type, in: query, schema: { type: string, enum: [schedule, manual] } }
        - name: status
          in: query
          description: Repeat to select one or more DeploymentRun creation outcomes. The current response object does not expose this internal status field.
          schema: { type: string, enum: [pending, succeeded, failed] }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 1000, default: 20 }
        - name: page
          in: query
          schema: { type: string }
      responses:
        '200':
          description: DeploymentRun list
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/DeploymentRun' }
                  next_page: { type: string, nullable: true }

  /v1/deployment_runs/{id}:
    get:
      operationId: getGlobalDeploymentRun
      tags: [Deployments]
      summary: Get DeploymentRun by ID
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string, pattern: '^drun_' }
      responses:
        '200':
          description: DeploymentRun details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeploymentRun'

  /v1/credentials:
    post:
      operationId: createCredential
      tags: [Credentials]
      summary: Create credential
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCredentialRequest'
      responses:
        '201':
          description: Credential created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Credential'
    get:
      operationId: listCredentials
      tags: [Credentials]
      summary: List credentials
      parameters:
        - name: agent_id
          in: query
          schema: { type: string }
      responses:
        '200':
          description: Credential list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Credential'
  /v1/credentials/{id}:
    get:
      operationId: getCredential
      tags: [Credentials]
      summary: Get credential (masked value)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Credential details (secret masked)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Credential'
    patch:
      operationId: updateCredential
      tags: [Credentials]
      summary: Update credential metadata
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCredentialRequest'
      responses:
        '200':
          description: Updated credential
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Credential'
  /v1/credentials/{id}/rotate:
    post:
      operationId: rotateCredential
      tags: [Credentials]
      summary: Rotate credential value
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [value]
              properties:
                value: { type: string, description: "New plaintext value" }
      responses:
        '200':
          description: Rotated credential
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Credential'
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: "API key with `sk-sb-` prefix"

  parameters:
    EndpointId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Service Endpoint ID
    AgentId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Agent ID
    SessionId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Session ID
    DeploymentId:
      name: id
      in: path
      required: true
      schema:
        type: string
        pattern: '^depl_'
      description: Deployment ID
    EnvironmentId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Environment ID
    SandboxId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Sandbox ID

  schemas:
    # --- Tasks ----------------------------------------------------
    TaskCostResponse:
      type: object
      required: [id, status, settled, currency, cost, estimated_cost, usage]
      properties:
        id:
          type: string
          description: SandBase task ID.
        status:
          type: string
          enum: [pending, running, completed, failed, timeout]
        settled:
          type: boolean
          description: Whether final cost settlement has completed.
        currency:
          type: string
          enum: [USD]
        cost:
          type: string
          pattern: '^\d+\.\d{6}$'
          description: USD amount with six decimal places. Not final while settled is false.
          example: '0.001234'
        estimated_cost:
          type: string
          pattern: '^\d+\.\d{6}$'
          description: Best-effort USD estimate with six decimal places. Informational while settled is false; matches cost once settled is true.
          example: '0.001234'
        usage:
          $ref: '#/components/schemas/TaskCostUsage'

    TaskCostUsage:
      type: object
      required: [prompt_tokens, completion_tokens, total_tokens, cached_tokens, cache_creation_tokens, reasoning_tokens]
      properties:
        prompt_tokens:
          type: integer
          minimum: 0
        completion_tokens:
          type: integer
          minimum: 0
        total_tokens:
          type: integer
          minimum: 0
        cached_tokens:
          type: integer
          minimum: 0
        cache_creation_tokens:
          type: integer
          minimum: 0
        reasoning_tokens:
          type: integer
          minimum: 0

    TaskCostError:
      type: object
      required: [error]
      properties:
        error:
          type: string

    # --- Models ---------------------------------------------------
    ModelListResponse:
      type: object
      required: [data, total, page, page_size]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ModelCatalogItem'
        total:
          type: integer
        page:
          type: integer
        page_size:
          type: integer

    ModelCatalogItem:
      type: object
      description: Lightweight model catalog item. Detailed token/cache/reasoning price fields are intentionally omitted from list responses.
      properties:
        id:
          type: string
        name:
          type: string
          example: openai/gpt-4o
        display_name:
          type: string
          example: GPT-4o
        vendor:
          type: string
        type:
          type: string
        description:
          type: string
        capability_tags:
          type: array
          items:
            type: string
        execution_mode:
          type: string
        context_length:
          type: integer
        published_at:
          type: string
          format: date-time
          nullable: true
        vendor_slug:
          type: string
        model_slug:
          type: string
        enabled:
          type: boolean
        featured:
          type: boolean
        base_price:
          oneOf:
            - type: string
            - type: number
          nullable: true
        price_formula:
          type: string
          nullable: true

    ModelDetail:
      allOf:
        - $ref: '#/components/schemas/ModelCatalogItem'
        - type: object
          description: Detailed model response. Full pricing fields are returned in model_card; there is no top-level pricing object.
          properties:
            skills:
              type: array
              description: Capability-derived skill tags for the model.
              items:
                type: string
            unified_schema:
              type: object
              additionalProperties: true
            supported_modes:
              type: array
              items:
                type: string
            run_count:
              type: integer
            sort_order:
              type: integer
            created_at:
              type: string
              format: date-time
            model_card:
              $ref: '#/components/schemas/ModelCard'
            examples:
              type: array
              items:
                type: object

    ModelCard:
      type: object
      description: Model pricing and presentation data.
      properties:
        base_price:
          oneOf:
            - type: string
            - type: number
          nullable: true
        prompt_token_price:
          oneOf:
            - type: string
            - type: number
          nullable: true
        completion_token_price:
          oneOf:
            - type: string
            - type: number
          nullable: true
        cache_read_multiplier:
          oneOf:
            - type: string
            - type: number
          nullable: true
        cache_write_multiplier:
          oneOf:
            - type: string
            - type: number
          nullable: true
        reasoning_price:
          oneOf:
            - type: string
            - type: number
          nullable: true
        price_formula:
          type: string
          nullable: true
        markup_ratio:
          oneOf:
            - type: string
            - type: number
          nullable: true
        readme:
          type: string
          nullable: true
        cover_url:
          type: string
          nullable: true

    # --- Chat Completions -----------------------------------------
    ChatCompletionRequest:
      type: object
      required: [model, messages]
      properties:
        model:
          type: string
          description: Model identifier
          example: "gpt-4o"
        messages:
          type: array
          items:
            type: object
            required: [role, content]
            properties:
              role:
                type: string
                enum: [system, user, assistant, tool]
              content:
                oneOf:
                  - type: string
                  - type: array
                    items:
                      type: object
              name:
                type: string
              tool_calls:
                type: array
              tool_call_id:
                type: string
        temperature:
          type: number
          minimum: 0
          maximum: 2
          default: 1
        top_p:
          type: number
        max_tokens:
          type: integer
        stream:
          type: boolean
          default: false
        stop:
          oneOf:
            - type: string
            - type: array
              items:
                type: string
        tools:
          type: array
          items:
            type: object
        mcp_servers:
          type: array
          maxItems: 20
          items: { $ref: '#/components/schemas/MCPServer' }
        tool_choice:
          oneOf:
            - type: string
            - type: object
        response_format:
          type: object
        n:
          type: integer
          default: 1
        presence_penalty:
          type: number
        frequency_penalty:
          type: number
        stream_options:
          type: object
          properties:
            include_usage:
              type: boolean

    ChatCompletionResponse:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum: [chat.completion]
        created:
          type: integer
        model:
          type: string
        choices:
          type: array
          items:
            type: object
            properties:
              index:
                type: integer
              message:
                type: object
                properties:
                  role:
                    type: string
                  content:
                    type: string
                  tool_calls:
                    type: array
              finish_reason:
                type: string
                enum: [stop, length, tool_calls, content_filter]
        usage:
          type: object
          properties:
            prompt_tokens:
              type: integer
            completion_tokens:
              type: integer
            total_tokens:
              type: integer

    # --- Embeddings -----------------------------------------------
    EmbeddingRequest:
      type: object
      required: [model, input]
      properties:
        model:
          type: string
          description: Embedding model identifier.
          example: "alibaba/text-embedding-v4"
        input:
          description: Text or list of texts to embed.
          oneOf:
            - type: string
              example: "SandBase provides a unified API for AI models, agents, and developer tools."
            - type: array
              items:
                type: string
              example:
                - "First document"
                - "Second document"
        dimensions:
          type: integer
          description: Optional output embedding dimension when the model supports dimension selection.
          enum: [2048, 1536, 1024, 768, 512, 256, 128, 64]
          example: 1024
        encoding_format:
          type: string
          description: Embedding encoding format.
          enum: [float]
          default: float

    EmbeddingResponse:
      type: object
      properties:
        object:
          type: string
          enum: [list]
        data:
          type: array
          items:
            type: object
            properties:
              object:
                type: string
                enum: [embedding]
              index:
                type: integer
              embedding:
                type: array
                items:
                  type: number
                  format: float
        model:
          type: string
          example: "alibaba/text-embedding-v4"
        usage:
          type: object
          properties:
            prompt_tokens:
              type: integer
            total_tokens:
              type: integer

    # --- Services (Endpoint resources) -----------------------------
    EndpointRuntimeProfile:
      type: object
      required: [id, display_name, enabled, supports_skills, supports_state, protocols]
      properties:
        id:
          type: string
          example: hermes
        display_name:
          type: string
          example: Hermes
        enabled:
          type: boolean
        supports_skills:
          type: boolean
        supports_state:
          type: boolean
        protocols:
          type: array
          items:
            type: string
            enum: [rest, acp, mcp]

    CreateDeclarativeEndpointRequest:
      type: object
      additionalProperties: false
      required: [name, runtime]
      properties:
        name:
          type: string
          maxLength: 200
        runtime:
          type: string
          description: Enabled runtime profile. This release supports hermes.
          example: hermes
        description:
          type: string
        model:
          oneOf:
            - type: string
            - type: object
              additionalProperties: true
        system:
          type: string
        skills:
          type: [array, 'null']
          maxItems: 20
          description: |
            Full vendor_slug/plugin_slug references. Hermes runtime identity and installation lookup use each
            bundle-root SKILL.md frontmatter name; the plugin slug and runtime name may differ.
          items:
            type: string
            pattern: '^[A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._-]*$'
        tools:
          type: [array, 'null']
          maxItems: 1
          items:
            $ref: '#/components/schemas/EndpointToolset'
        protocols:
          type: array
          items:
            type: string
            enum: [rest, acp, mcp]
          default: [rest]

    CreateAdvancedEndpointRequest:
      type: object
      additionalProperties: false
      required: [name, agent_id]
      properties:
        name:
          type: string
        slug:
          type: string
        agent_id:
          type: string
        agent_version:
          type: integer
          minimum: 1
        environment_id:
          type: string
          deprecated: true
          description: Optional compatibility assertion; the Agent runtime Environment is resolved automatically.
        protocols:
          type: array
          items:
            type: string
            enum: [rest, acp, mcp]
        config:
          type: object
          additionalProperties: true
        session_metadata:
          type: object
          additionalProperties: true
        memory_config:
          type: object
          additionalProperties: true
        resource_config:
          type: object
          additionalProperties: true
        vault_config:
          type: object
          additionalProperties: true

    EndpointToolset:
      type: object
      additionalProperties: false
      required: [type]
      properties:
        type:
          type: string
          const: agent_toolset_20260401

    UpdateEndpointRequest:
      type: object
      properties:
        name:
          type: string
        status:
          type: string
          enum: [active, disabled]
        protocols:
          type: array
          items:
            type: string
            enum: [rest, acp, mcp]

    Endpoint:
      type: object
      required: [id, type, name, agent_id, environment_id, protocols, status, creation_mode, run_url]
      properties:
        id:
          type: string
          example: ep_01abc
        type:
          type: string
          const: endpoint
        name:
          type: string
        slug:
          type: string
        agent_id:
          type: string
        agent_version:
          type: [integer, 'null']
        environment_id:
          type: string
        protocols:
          type: array
          items:
            type: string
            enum: [rest, acp, mcp]
        status:
          type: string
          enum: [active, disabled]
        creation_mode:
          type: string
          enum: [declarative, advanced]
        runtime:
          type: string
          description: Present for declarative Endpoints.
        skills:
          type: array
          description: Present for declarative Endpoints.
          items:
            type: string
        run_url:
          type: string
          format: uri
        acp_url:
          type: string
          format: uri
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    EndpointInvokeRequest:
      type: object
      properties:
        input:
          oneOf:
            - type: string
            - type: array
              items:
                type: object
                additionalProperties: true
        content:
          type: array
          items:
            type: object
            additionalProperties: true
        session_id:
          type: string
          description: Persistent Session to continue. Omit to create a new Session.
      anyOf:
        - required: [input]
        - required: [content]

    EndpointInvokeAccepted:
      type: object
      description: Acceptance response for standard events sent to a persistent Session.
      required: [session_id, events]
      properties:
        session_id:
          type: string
          description: Persistent public Session identity.
        events:
          type: array
          items:
            $ref: '#/components/schemas/SessionEvent'

    APIError:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [type, message]
          properties:
            type:
              type: string
            message:
              type: string

    # --- Agents ---------------------------------------------------
    CreateAgentRequest:
      type: object
      required: [model, name]
      properties:
        model:
          oneOf:
            - type: string
              example: "claude-sonnet-4"
            - type: object
              properties:
                id:
                  type: string
                speed:
                  type: string
                  enum: [standard, fast]
                effort:
                  oneOf:
                    - type: string
                      enum: [low, medium, high, xhigh, max]
                    - type: object
                      required: [type]
                      properties:
                        type: { type: string, enum: [low, medium, high, xhigh, max] }
        name:
          type: string
          maxLength: 256
        description:
          type: string
          maxLength: 2048
        system:
          type: string
          maxLength: 100000
        tools:
          type: array
          maxItems: 128
          items:
            type: object
        skills:
          type: array
          maxItems: 20
          items:
            type: object
        multiagent:
          type: object
          description: Reserved for Claude compatibility; non-null values currently return unsupported_feature.
        metadata:
          type: object
          additionalProperties:
            type: string

    UpdateAgentRequest:
      type: object
      properties:
        version:
          type: integer
          description: Current version (optimistic lock)
        name:
          type: string
        description:
          type: string
        model:
          oneOf:
            - type: string
            - type: object
        system:
          type: string
        tools:
          type: array
        skills:
          type: array
        mcp_servers:
          type: array
        multiagent:
          type: object
          description: Reserved; non-null values currently return unsupported_feature.
        metadata:
          type: object

    Agent:
      type: object
      properties:
        id:
          type: string
        type:
          type: string
          enum: [agent]
        name:
          type: string
        description:
          type: string
        model:
          type: object
          required: [id]
          properties:
            id: { type: string }
            effort:
              type: object
              nullable: true
              properties:
                type: { type: string, enum: [low, medium, high, xhigh, max] }
            speed: { type: string, enum: [standard, fast], nullable: true }
        system:
          type: string
        tools:
          type: array
        mcp_servers:
          type: array
        skills:
          type: array
        multiagent:
          type: object
        runtime_profile:
          type: string
          readOnly: true
          description: Runtime selected when the Agent is created; immutable for its lifetime.
        metadata:
          type: object
        version:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        archived_at:
          type: string
          format: date-time
          nullable: true

    AgentListPage:
      type: object
      required: [data, next_page]
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Agent' }
        next_page: { type: string, nullable: true }

    MCPServer:
      type: object
      required: [name, type, url]
      properties:
        name: { type: string, minLength: 1, maxLength: 255 }
        type: { type: string, enum: [url] }
        url: { type: string, format: uri }

    # --- Sessions -------------------------------------------------
    CreateSessionRequest:
      type: object
      additionalProperties: false
      required: [agent]
      properties:
        agent:
          oneOf:
            - type: string
              description: Agent ID (uses latest version)
            - type: object
              additionalProperties: false
              required: [type, id]
              properties:
                type:
                  type: string
                  enum: [agent]
                id:
                  type: string
                version:
                  type: integer
                  minimum: 1
        agent_version_id:
          type: string
          description: Immutable AgentVersion resource ID. Takes precedence over agent.version when supplied.
        environment_id:
          type: string
          description: Optional authorized per-Session override. Omit to use the Agent-owned default Environment.
        initial_events:
          type: array
          maxItems: 50
          items:
            $ref: '#/components/schemas/SessionEventInput'
        initial_event:
          type: array
          deprecated: true
          description: SandBase legacy alias for initial_events; do not combine both fields.
          maxItems: 50
          items:
            $ref: '#/components/schemas/SessionEventInput'
        title:
          type: string
        metadata:
          type: object
        resources:
          type: array
          description: Reserved for Claude compatibility; rejected until resource attachment is supported.
          items: { type: object, additionalProperties: true }
        vault_ids:
          type: array
          description: Reserved for Claude compatibility; rejected until vault attachment is supported.
          items: { type: string }

    Session:
      type: object
      required: [id, type, status, agent, agent_id, agent_version, agent_version_id, environment_id, source, created_at]
      properties:
        id:
          type: string
          description: Persistent public Session identity.
        type:
          type: string
          enum: [session]
        agent_id:
          type: string
        agent:
          allOf:
            - $ref: '#/components/schemas/Agent'
          readOnly: true
        agent_version:
          type: integer
          nullable: true
        agent_version_id:
          type: string
        environment_id:
          type: string
        title:
          type: string
        status:
          type: string
          enum: [idle, running, rescheduling, terminated]
        source:
          type: string
          enum: [direct, endpoint, deployment]
        metadata:
          type: object
        archived_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    SendEventsRequest:
      type: object
      required: [events]
      properties:
        events:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/SessionEventInput'

    SessionAgentUpdate:
      type: object
      description: Session-local full replacement override; omitted fields preserve the pinned Agent snapshot.
      properties:
        tools:
          type: array
          items: { type: object, additionalProperties: true }
        mcp_servers:
          type: array
          items: { $ref: '#/components/schemas/MCPServer' }

    SessionEventInput:
      description: >-
        Discriminated input schema. Recognition does not imply runtime support:
        user.tool_confirmation, user.custom_tool_result, user.tool_result, and
        user.define_outcome currently return HTTP 422 unsupported_event_type;
        any session_thread_id returns HTTP 422 unsupported_multiagent. The
        rejected batch is not persisted.
      discriminator:
        propertyName: type
        mapping:
          user.message: '#/components/schemas/UserMessageEventInput'
          user.interrupt: '#/components/schemas/UserInterruptEventInput'
          user.tool_confirmation: '#/components/schemas/UserToolConfirmationEventInput'
          user.custom_tool_result: '#/components/schemas/UserCustomToolResultEventInput'
          user.tool_result: '#/components/schemas/UserToolResultEventInput'
          user.define_outcome: '#/components/schemas/UserDefineOutcomeEventInput'
          interrupt.response: '#/components/schemas/SandBaseInterruptResponseEventInput'
          system.message: '#/components/schemas/SandBaseSystemMessageEventInput'
      oneOf:
        - $ref: '#/components/schemas/UserMessageEventInput'
        - $ref: '#/components/schemas/UserInterruptEventInput'
        - $ref: '#/components/schemas/UserToolConfirmationEventInput'
        - $ref: '#/components/schemas/UserCustomToolResultEventInput'
        - $ref: '#/components/schemas/UserToolResultEventInput'
        - $ref: '#/components/schemas/UserDefineOutcomeEventInput'
        - $ref: '#/components/schemas/SandBaseInterruptResponseEventInput'
        - $ref: '#/components/schemas/SandBaseSystemMessageEventInput'

    UserMessageEventInput:
      type: object
      additionalProperties: false
      required: [type, content]
      properties:
        type: { type: string, enum: [user.message] }
        content: { $ref: '#/components/schemas/UserMessageContent' }

    UserInterruptEventInput:
      type: object
      additionalProperties: false
      required: [type]
      properties:
        type: { type: string, enum: [user.interrupt] }
        session_thread_id: { type: string }

    UserToolConfirmationEventInput:
      type: object
      description: Schema-recognized but not runtime-supported; requests currently return HTTP 422 unsupported_event_type without persistence.
      additionalProperties: false
      required: [type, tool_use_id, result]
      properties:
        type: { type: string, enum: [user.tool_confirmation] }
        tool_use_id: { type: string }
        result: { type: string, enum: [allow, deny] }
        deny_message: { type: string, description: Allowed only when result is deny. }
        session_thread_id: { type: string }

    UserCustomToolResultEventInput:
      type: object
      description: Schema-recognized but not runtime-supported; requests currently return HTTP 422 unsupported_event_type without persistence.
      additionalProperties: false
      required: [type, custom_tool_use_id]
      properties:
        type: { type: string, enum: [user.custom_tool_result] }
        custom_tool_use_id: { type: string }
        content: { $ref: '#/components/schemas/ToolResultContent' }
        is_error: { type: boolean }
        session_thread_id: { type: string }

    UserToolResultEventInput:
      type: object
      description: Schema-recognized but not runtime-supported; requests currently return HTTP 422 unsupported_event_type without persistence.
      additionalProperties: false
      required: [type, tool_use_id]
      properties:
        type: { type: string, enum: [user.tool_result] }
        tool_use_id: { type: string }
        content: { $ref: '#/components/schemas/ToolResultContent' }
        is_error: { type: boolean }
        session_thread_id: { type: string }

    UserDefineOutcomeEventInput:
      type: object
      description: Schema-recognized but not runtime-supported; requests currently return HTTP 422 unsupported_event_type without persistence.
      additionalProperties: false
      required: [type, description, rubric]
      properties:
        type: { type: string, enum: [user.define_outcome] }
        description: { type: string }
        rubric: { $ref: '#/components/schemas/OutcomeRubric' }
        max_iterations: { type: integer, minimum: 1, maximum: 20, default: 3 }

    SandBaseInterruptResponseEventInput:
      type: object
      additionalProperties: false
      description: SandBase extension; not part of the Claude Managed Agents event input union.
      required: [type, interrupt_id, response]
      properties:
        type: { type: string, enum: [interrupt.response] }
        interrupt_id: { type: string }
        response: { type: object, additionalProperties: true }

    SandBaseSystemMessageEventInput:
      type: object
      additionalProperties: false
      description: SandBase extension for a privileged mid-conversation system message.
      required: [type, content]
      properties:
        type: { type: string, enum: [system.message] }
        content:
          type: array
          minItems: 1
          items: { $ref: '#/components/schemas/TextContentBlock' }

    UserMessageContent:
      type: array
      minItems: 1
      items:
        oneOf:
          - $ref: '#/components/schemas/TextContentBlock'
          - $ref: '#/components/schemas/ImageContentBlock'
          - $ref: '#/components/schemas/DocumentContentBlock'

    ToolResultContent:
      type: array
      minItems: 1
      items:
        oneOf:
          - $ref: '#/components/schemas/TextContentBlock'
          - $ref: '#/components/schemas/ImageContentBlock'
          - $ref: '#/components/schemas/DocumentContentBlock'
          - $ref: '#/components/schemas/SearchResultContentBlock'

    TextContentBlock:
      type: object
      additionalProperties: false
      required: [type, text]
      properties:
        type: { type: string, enum: [text] }
        text: { type: string }

    ImageContentBlock:
      type: object
      additionalProperties: false
      required: [type, source]
      properties:
        type: { type: string, enum: [image] }
        source: { type: object, additionalProperties: true }

    DocumentContentBlock:
      type: object
      additionalProperties: false
      required: [type, source]
      properties:
        type: { type: string, enum: [document] }
        source: { type: object, additionalProperties: true }
        context: { type: string }
        title: { type: string }

    SearchResultContentBlock:
      type: object
      additionalProperties: false
      required: [type, source, title, content]
      properties:
        type: { type: string, enum: [search_result] }
        source: { type: string }
        title: { type: string }
        content:
          type: array
          items: { $ref: '#/components/schemas/TextContentBlock' }
        citations: { type: object, additionalProperties: true }

    OutcomeRubric:
      oneOf:
        - $ref: '#/components/schemas/TextOutcomeRubric'
        - $ref: '#/components/schemas/FileOutcomeRubric'

    TextOutcomeRubric:
      type: object
      additionalProperties: false
      required: [type, content]
      properties:
        type: { type: string, enum: [text] }
        content: { type: string, maxLength: 262144 }

    FileOutcomeRubric:
      type: object
      additionalProperties: false
      required: [type, file_id]
      properties:
        type: { type: string, enum: [file] }
        file_id: { type: string }

    AcceptedSessionEvents:
      type: object
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/SessionEvent'

    SessionEvent:
      type: object
      required: [id, type, processed_at]
      properties:
        id:
          type: string
          pattern: '^sevt_'
        type:
          type: string
        content:
          type: array
          items: { type: object, additionalProperties: true }
        data:
          type: object
          description: Present only for event types whose canonical payload is not content.
        stop_reason:
          type: object
          nullable: true
        model_used:
          type: string
          nullable: true
        tokens_in:
          type: integer
        tokens_out:
          type: integer
        duration_ms:
          type: integer
          nullable: true
        processed_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time

    CreateCredentialRequest:
      type: object
      required: [scope, scope_name, secret_key, value]
      properties:
        agent_id: { type: string }
        scope: { type: string }
        scope_name: { type: string }
        secret_key: { type: string }
        value: { type: string, writeOnly: true, description: Plaintext value encrypted at rest and never returned. }
        group_key: { type: string }
        strategy: { type: string, default: round_robin }
        weight: { type: integer, default: 1 }

    UpdateCredentialRequest:
      type: object
      minProperties: 1
      properties:
        status: { type: string, enum: [active, disabled] }
        strategy: { type: string }
        weight: { type: integer }

    Credential:
      type: object
      description: Credential metadata. The plaintext and encrypted payload are never returned.
      required: [id, scope, scope_name, secret_key, value_hint, status, created_at, updated_at]
      properties:
        id: { type: string }
        agent_id: { type: string }
        agent_version: { type: integer, nullable: true }
        scope: { type: string }
        scope_name: { type: string }
        secret_key: { type: string }
        value_hint: { type: string, description: Non-secret masked hint. }
        group_key: { type: string }
        strategy: { type: string }
        weight: { type: integer }
        status: { type: string, enum: [active, disabled] }
        last_used_at: { type: string, format: date-time, nullable: true }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    CreateDeploymentRequest:
      type: object
      required: [name, agent_id, initial_events]
      properties:
        name: { type: string }
        description: { type: string }
        agent_id:
          type: string
          description: Agent to run. This is the primary request field.
        agent:
          deprecated: true
          description: Compatibility alias for agent_id; do not send both fields.
          oneOf:
            - type: string
            - $ref: '#/components/schemas/AgentReference'
        environment_id:
          type: string
        initial_events:
          type: array
          items: { $ref: '#/components/schemas/SessionEventInput' }
        schedule: { type: object, nullable: true }
        resources: { type: array, items: { type: object, additionalProperties: true } }
        vault_ids: { type: array, items: { type: string, pattern: '^vlt_' } }
        metadata: { type: object, additionalProperties: { type: string } }
        config: { type: object }

    UpdateDeploymentRequest:
      type: object
      minProperties: 1
      additionalProperties: false
      properties:
        name: { type: string }
        description: { type: string }
        metadata: { type: object, additionalProperties: true }
        agent:
          oneOf:
            - type: string
            - $ref: '#/components/schemas/AgentReference'
        environment_id: { type: string }
        initial_events:
          type: array
          items: { $ref: '#/components/schemas/SessionEventInput' }
        schedule: { type: object, nullable: true }
        resources:
          type: array
          description: Reserved for Claude compatibility; rejected until resource attachment is supported.
          items: { type: object, additionalProperties: true }
        vault_ids:
          type: array
          description: Reserved for Claude compatibility; rejected until vault attachment is supported.
          items: { type: string }
        timeout_policy:
          type: object
          additionalProperties: false
          properties:
            hard_timeout_sec: { type: integer, minimum: 60, maximum: 604800, nullable: true }
            inactivity_timeout_sec: { type: integer, minimum: 180, maximum: 3600 }
        expected_version: { type: integer, minimum: 1 }
        agent_binding:
          type: object
          additionalProperties: false
          required: [agent_id]
          properties:
            agent_id: { type: string }
            agent_version: { type: integer, minimum: 1 }
            environment_id: { type: string }
        agent_config: { type: object, additionalProperties: true }
        notification_settings:
          type: object
          additionalProperties: false
          properties:
            feishu_webhook_url: { type: string, format: uri, nullable: true, writeOnly: true }

    Deployment:
      allOf:
        - $ref: '#/components/schemas/CreateDeploymentRequest'
        - type: object
          required: [id, type, agent, status, created_at, updated_at]
          properties:
            id: { type: string, pattern: '^depl_' }
            type: { type: string, enum: [deployment] }
            agent: { $ref: '#/components/schemas/AgentReference' }
            status: { type: string, enum: [active, paused, archived] }
            paused_reason: { type: object, nullable: true, additionalProperties: true }
            archived_at: { type: string, format: date-time, nullable: true }
            created_at: { type: string, format: date-time }
            updated_at: { type: string, format: date-time }

    DeploymentRun:
      type: object
      description: Durable result of one Deployment trigger's Session creation attempt; it never mirrors later Session state.
      required: [id, type, agent, deployment_id, session_id, error, trigger_context, created_at]
      properties:
        id: { type: string, pattern: '^drun_' }
        type: { type: string, enum: [deployment_run] }
        agent: { $ref: '#/components/schemas/AgentReference' }
        deployment_id: { type: string, pattern: '^depl_' }
        session_id: { type: string, pattern: '^sess_', nullable: true }
        error:
          type: object
          nullable: true
          required: [type, code, message]
          properties:
            type: { type: string }
            code: { type: string }
            message: { type: string }
        trigger_context:
          oneOf:
            - type: object
              required: [type]
              properties: { type: { type: string, enum: [manual] } }
            - type: object
              required: [type, scheduled_at]
              properties:
                type: { type: string, enum: [schedule] }
                scheduled_at: { type: string, format: date-time }
        created_at: { type: string, format: date-time }

    AgentReference:
      type: object
      required: [id, type, version]
      properties:
        id: { type: string }
        type: { type: string, enum: [agent] }
        version: { type: integer, minimum: 1 }

    # --- Environments ---------------------------------------------
    CreateEnvironmentRequest:
      type: object
      required: [name]
      properties:
        name:
          type: string
        description:
          type: string
        config:
          $ref: '#/components/schemas/EnvironmentConfig'
        scope: { type: string, enum: [organization, account] }
        metadata:
          type: object
        credential_bindings:
          type: array
          items:
            type: object
            required: [credential_id, target]
            properties:
              credential_id: { type: string }
              purpose: { type: string }
              target: { type: string, pattern: '^[A-Z_][A-Z0-9_]{0,127}$' }

    UpdateEnvironmentRequest:
      type: object
      minProperties: 1
      properties:
        name:
          type: string
        description:
          type: string
        config:
          type: object
        metadata:
          type: object
        credential_bindings:
          type: array
          items:
            type: object
            required: [credential_id, target]
            properties:
              credential_id: { type: string }
              purpose: { type: string }
              target: { type: string, pattern: '^[A-Z_][A-Z0-9_]{0,127}$' }

    Environment:
      type: object
      required: [id, type, name, config, metadata, created_at, updated_at]
      properties:
        id:
          type: string
        type:
          type: string
          enum: [environment]
        agent_id:
          type: string
          nullable: true
          readOnly: true
          description: Owning Agent. Agent-owned Environments are never shared by multiple Agents.
        name:
          type: string
        description:
          type: string
        config:
          $ref: '#/components/schemas/EnvironmentConfig'
          description: Mutable environment configuration. Its runtime/base template cannot change for an Agent-owned Environment.
        scope: { type: string, enum: [organization, account], nullable: true }
        metadata:
          type: object
        credential_bindings:
          type: array
          items:
            type: object
        archived_at:
          type: string
          format: date-time
          nullable: true
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    EnvironmentConfig:
      oneOf:
        - type: object
          required: [type]
          properties:
            type: { type: string, enum: [self_hosted] }
        - type: object
          required: [type]
          properties:
            type: { type: string, enum: [cloud] }
            networking: { type: object, additionalProperties: true }
            packages: { type: object, additionalProperties: true }
            base_template:
              type: string
              description: SandBase extension for a managed runtime preset.
            resources:
              type: object
              description: SandBase extension for managed runtime sizing.
              additionalProperties: true
    # --- Sandboxes ------------------------------------------------
    CreateSandboxRequest:
      type: object
      required: [templateID]
      properties:
        templateID:
          type: string
          description: Template identifier
          enum: [code_interpreter, claude, codex, opencode, openclaw, hermes-agent, desktop, base]
        timeout:
          type: integer
          description: Timeout in seconds
          default: 300
        metadata:
          type: object
          additionalProperties:
            type: string
        envVars:
          type: object
          additionalProperties:
            type: string

    Sandbox:
      type: object
      properties:
        sandboxID:
          type: string
        templateID:
          type: string
        clientID:
          type: string
        metadata:
          type: object
        startedAt:
          type: string
          format: date-time
        endAt:
          type: string
          format: date-time
        status:
          type: string
          enum: [creating, running, paused, stopped, error]
