# -----------------------------------------------------------------------------
# Source of Truth (OpenAPI artifact)
#
# This file MUST reflect actual HTTP behavior. Regenerate with: pnpm openapi:gen
# If apps/api/src/router.ts, apps/api/src/contracts/, or packages/sdk/src/ change
# API behavior → update apps/api/scripts/generate_openapi.mjs and/or docs, then
# commit the regenerated openapi.yaml in the SAME PR (see scripts/check_docs_drift.mjs).
# -----------------------------------------------------------------------------

openapi: 3.0.3
info:
  title: MemoryNode API
  version: 1.0.0
  description: >-
    MemoryNode — reliable per-user memory for customer-facing AI (support bots, SMB chat, SaaS copilots). Store, search,
    and prompt-ready context over HTTPS; hybrid retrieval is server-managed.


    This file is produced by `pnpm openapi:gen` from `apps/api/scripts/generate_openapi.mjs` (schemas aligned with
    `apps/api/src/contracts/`).


    **Also implemented in the Worker but omitted from this contract:** `/ready`, `/mcp` (see MCP docs), PayU
    `/v1/billing/webhook`, admin `/admin/*` and `/v1/admin/*`, dashboard overview, connectors,
    conversation/ingest/links, search history/replay, profile pins, context feedback, pruning metrics, and other
    operator paths. See `apps/api/src/router.ts` and `workerApp.ts` for the full surface.
  x-doc-governance: "SOURCE_OF_TRUTH: Regenerate via `pnpm openapi:gen` when `apps/api/scripts/generate_openapi.mjs`,
    `apps/api/src/contracts/`, or `apps/api/src/router.ts` change. Same PR as behavioral API changes. Human prose:
    `docs/external/API_USAGE.md`."
servers:
  - url: https://api.memorynode.ai
    description: Production
  - url: http://127.0.0.1:8787
    description: Local development
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: 'Worker API key. Pass via "Authorization: Bearer <key>" or "x-api-key" header.'
  schemas:
    MemoryInsertResponse:
      type: object
      properties:
        memory_id:
          type: string
          example: mem_abc123
        stored:
          type: boolean
          enum:
            - true
          description: Always true on HTTP 200 — your memory row was persisted.
        deduped:
          type: boolean
          description: True when an existing memory was returned instead of inserting a duplicate.
        dedupe_kind:
          type: string
          enum:
            - near
            - semantic
          description: Present when deduped is true.
        chunks:
          type: integer
          description: Number of search-indexed chunks created for this write. Omitted when embedding was skipped (e.g. budget
            text-only ingest); use `stored` + `embedding` instead.
          example: 3
        embedding:
          type: string
          enum:
            - skipped_due_to_budget
          description: Present when the memory row was saved but vector embedding was skipped (e.g. workspace AI budget). Search
            may not return this text until re-embedded.
        extraction:
          type: object
          properties:
            status:
              type: string
              enum:
                - run
                - degraded
                - skipped
            reason:
              type: string
              enum:
                - user_disabled
                - low_importance
                - plan_limit
                - entitlement_degraded
                - budget_limit
                - extraction_error
                - none
              description: Only present when status is skipped (or extraction_error details).
            error:
              type: string
          required:
            - status
          description: What happened with automatic fact extraction for this write.
        safety:
          type: object
          properties:
            pii_hints:
              type: array
              items:
                type: string
                enum:
                  - email
                  - phone
          required:
            - pii_hints
          description: 'Present when request included header x-safety-pii-scan: "1" and hints were found.'
        intelligence:
          type: object
          properties:
            canonical_hash:
              type: string
            confidence:
              type: number
            source_weight:
              type: number
            priority_score:
              type: number
            priority_tier:
              type: string
            auto_pinned:
              type: boolean
            conflict_state:
              type: string
              enum:
                - none
                - resolved
          description: Lifecycle/conflict signals on a newly inserted memory.
        superseded_memory_id:
          type: string
          format: uuid
        trace:
          type: object
          properties:
            memory_evolution:
              type: string
            memory_evolution_applied:
              type: boolean
      required:
        - memory_id
        - stored
        - extraction
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: BAD_REQUEST
            message:
              type: string
              example: Validation failed
            details:
              type: object
              additionalProperties:
                type: array
                items:
                  type: string
              example:
                text:
                  - Required
          required:
            - code
            - message
        request_id:
          type: string
          example: req_abc123
      required:
        - error
        - request_id
    MemoryInsertPayload:
      type: object
      properties:
        userId:
          type: string
          minLength: 1
          example: user_42
        user_id:
          type: string
          minLength: 1
          example: user_42
        owner_id:
          type: string
          minLength: 1
        owner_type:
          type: string
          enum:
            - user
            - team
            - app
        entity_id:
          type: string
          minLength: 1
        entity_type:
          type: string
          enum:
            - user
            - team
            - app
        scope:
          type: string
        namespace:
          type: string
          example: default
        containerTag:
          type: string
        text:
          type: string
          minLength: 1
          maxLength: 50000
          example: Meeting notes from standup...
        metadata:
          type: object
          additionalProperties:
            anyOf:
              - type: string
              - type: number
              - type: boolean
              - nullable: true
              - nullable: true
          example:
            project: alpha
            priority: 1
        memory_type:
          type: string
          enum: &a1
            - fact
            - preference
            - event
            - note
            - task
            - correction
            - pin
          description: Optional memory type tag (see MEMORY_TYPES in apps/api/src/contracts/search.ts).
        importance:
          type: number
          minimum: 0.01
          maximum: 100
          description: Optional ranking multiplier. Higher values increase retrieval likelihood.
        chunk_profile:
          type: string
          enum:
            - balanced
            - dense
            - document
          description: Chunking preset for long text before embedding.
        extract:
          type: boolean
          default: true
          description: When true (default), may run lightweight LLM extraction to child memories when plan and budget allow. Set
            false to store only the parent memory.
        effective_at:
          type: string
        replaces_memory_id:
          type: string
          format: uuid
        idempotency_key:
          type: string
          minLength: 8
          maxLength: 128
      required:
        - text
    SearchResultItem:
      type: object
      properties:
        chunk_id:
          type: string
        memory_id:
          type: string
        chunk_index:
          type: integer
        text:
          type: string
        score:
          type: number
        _explain:
          type: object
          properties:
            rrf_score:
              type: number
            match_sources:
              type: array
              items:
                type: string
                enum:
                  - vector
                  - text
            vector_score:
              type: number
            text_score:
              type: number
          required:
            - rrf_score
            - match_sources
      required:
        - chunk_id
        - memory_id
        - chunk_index
        - text
        - score
    SearchResponse:
      type: object
      properties:
        results:
          type: array
          items:
            $ref: "#/components/schemas/SearchResultItem"
        page:
          type: integer
        page_size:
          type: integer
        total:
          type: integer
        has_more:
          type: boolean
        retrieval_trace:
          type: object
          additionalProperties:
            nullable: true
      required:
        - results
        - page
        - total
        - has_more
    SearchPayload:
      type: object
      properties:
        userId:
          type: string
          minLength: 1
        user_id:
          type: string
          minLength: 1
          example: user_42
        owner_id:
          type: string
          minLength: 1
        owner_type:
          type: string
          enum:
            - user
            - team
            - app
        entity_id:
          type: string
          minLength: 1
        entity_type:
          type: string
          enum:
            - user
            - team
            - app
        scope:
          type: string
        namespace:
          type: string
          example: default
        containerTag:
          type: string
        query:
          type: string
          minLength: 1
          maxLength: 2000
          example: project status update
        top_k:
          type: integer
          minimum: 1
          maximum: 20
          example: 8
        page:
          type: integer
          minimum: 1
          example: 1
        page_size:
          type: integer
          minimum: 1
          maximum: 50
          example: 10
        filters:
          type: object
          properties:
            metadata:
              type: object
              additionalProperties:
                anyOf:
                  - type: string
                  - type: number
                  - type: boolean
                  - nullable: true
                  - nullable: true
            start_time:
              type: string
              example: 2025-01-01T00:00:00Z
            end_time:
              type: string
              example: 2025-12-31T23:59:59Z
            memory_type:
              anyOf:
                - type: string
                  enum: *a1
                - type: array
                  items:
                    type: string
                    enum: *a1
                  minItems: 1
              description: Single memory type or array (OR semantics).
            filter_mode:
              type: string
              enum:
                - and
                - or
              description: Metadata match mode (default and).
        explain:
          type: boolean
        search_mode:
          type: string
          enum:
            - hybrid
            - vector
            - keyword
        min_score:
          type: number
          minimum: 0
          maximum: 1
        retrieval_profile:
          type: string
          enum:
            - balanced
            - recall
            - precision
      required:
        - query
    ContextResponse:
      type: object
      properties:
        context_text:
          type: string
        citations:
          type: array
          items:
            type: object
            properties:
              i:
                type: integer
              chunk_id:
                type: string
              memory_id:
                type: string
              chunk_index:
                type: integer
            required:
              - i
              - chunk_id
              - memory_id
              - chunk_index
        page:
          type: integer
        page_size:
          type: integer
        total:
          type: integer
        has_more:
          type: boolean
        context_blocks:
          type: integer
      required:
        - context_text
        - citations
        - page
        - total
        - has_more
    UsageCapAlert:
      type: object
      properties:
        resource:
          type: string
          enum:
            - writes
            - reads
            - embeds
            - embed_tokens
            - extraction_calls
            - gen_tokens
            - storage
        severity:
          type: string
          enum:
            - warning
            - critical
        used:
          type: number
        cap:
          type: number
        ratio:
          type: number
      required:
        - resource
        - severity
        - used
        - cap
        - ratio
    UsageResponse:
      type: object
      properties:
        day:
          type: string
          example: 2026-04-18
        workspace_id:
          type: string
          example: 00000000-0000-4000-8000-000000000000
        plan:
          type: string
          example: launch
        entitlement_active:
          type: boolean
          example: true
        entitlement_source:
          type: string
          enum:
            - billing
            - internal_grant
          example: billing
        writes:
          type: integer
        reads:
          type: integer
        embeds:
          type: integer
        limits:
          type: object
          properties:
            writes:
              type: integer
            reads:
              type: integer
            embeds:
              type: integer
          required:
            - writes
            - reads
            - embeds
        caps:
          type: object
          properties:
            writes:
              type: integer
            reads:
              type: integer
            embeds:
              type: integer
          required:
            - writes
            - reads
            - embeds
          description: Legacy alias; prefer limits.
          deprecated: true
        cap_alerts:
          type: array
          items:
            $ref: "#/components/schemas/UsageCapAlert"
        operational_mode:
          type: string
          enum:
            - normal
            - degraded
            - sleep
        grace_soft_downgrade:
          type: boolean
          description: "True when entitlement is in billing grace: daily limits floored toward Launch while plan reflects the paid
            tier."
      required:
        - plan
        - writes
        - reads
        - embeds
    DashboardOpsEnvelope:
      type: object
      properties:
        ok:
          type: boolean
      required:
        - ok
    BillingStatusResponse:
      type: object
      properties:
        plan:
          type: string
          description: Legacy internal DB plan label (pro/team) for compatibility. Use effective_plan for display and quotas.
          example: pro
        plan_status:
          type: string
          example: active
        current_period_end:
          type: string
          nullable: true
          example: null
        cancel_at_period_end:
          type: boolean
          example: false
        effective_plan:
          type: string
          description: "Plan code for display: launch|build|deploy|scale|scale_plus."
          example: build
      required:
        - plan
        - plan_status
        - current_period_end
        - cancel_at_period_end
        - effective_plan
    BillingCheckoutPayload:
      type: object
      properties:
        plan:
          type: string
          enum:
            - launch
            - build
            - deploy
            - scale
          example: build
        firstname:
          type: string
        email:
          type: string
        phone:
          type: string
  parameters: {}
paths:
  /healthz:
    get:
      summary: Health check
      tags:
        - Health
      responses:
        "200":
          description: Service healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - ok
                  version:
                    type: string
                  build_version:
                    type: string
                  stage:
                    type: string
                  git_sha:
                    type: string
                required:
                  - status
                  - version
  /v1/health:
    get:
      summary: Health check (versioned)
      description: Returns service health and version. Same response as /healthz. No auth required. Prefer this for API
        clients that version endpoints.
      tags:
        - Health
      responses:
        "200":
          description: Service healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - ok
                  version:
                    type: string
                  build_version:
                    type: string
                  stage:
                    type: string
                  git_sha:
                    type: string
                  embedding_model:
                    type: string
                required:
                  - status
                  - version
  /v1/memories:
    post:
      summary: Ingest a memory
      tags:
        - Memories
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
            enum:
              - "1"
            description: When set to "1", response may include safety.pii_hints (heuristic email/phone hints only; not a DLP scan).
          required: false
          name: x-safety-pii-scan
          in: header
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MemoryInsertPayload"
      responses:
        "200":
          description: Memory ingested
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MemoryInsertResponse"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    get:
      summary: List memories with pagination and filters
      tags:
        - Memories
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
          required: false
          name: user_id
          in: query
        - schema:
            type: string
          required: false
          name: owner_id
          in: query
        - schema:
            type: string
          required: false
          name: entity_id
          in: query
        - schema:
            type: string
            enum:
              - user
              - team
              - app
          required: false
          name: owner_type
          in: query
        - schema:
            type: string
            enum:
              - user
              - team
              - app
          required: false
          name: entity_type
          in: query
        - schema:
            type: string
          required: false
          name: namespace
          in: query
        - schema:
            type: integer
            minimum: 1
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
          required: false
          name: page_size
          in: query
        - schema:
            type: string
            enum: *a1
          required: false
          name: memory_type
          in: query
        - schema:
            type: string
            description: JSON-encoded metadata filter
          required: false
          name: metadata
          in: query
        - schema:
            type: string
          required: false
          name: start_time
          in: query
        - schema:
            type: string
          required: false
          name: end_time
          in: query
      responses:
        "200":
          description: Paginated list of memories
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/memories/{id}:
    get:
      summary: Fetch a single memory by ID
      tags:
        - Memories
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
          required: true
          name: id
          in: path
      responses:
        "200":
          description: Memory object
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Memory not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    delete:
      summary: Delete a memory
      tags:
        - Memories
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
          required: true
          name: id
          in: path
      responses:
        "200":
          description: Memory deleted
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Memory not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/search:
    post:
      summary: Hybrid search (vector + full-text + RRF)
      tags:
        - Retrieval
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SearchPayload"
      responses:
        "200":
          description: Search results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchResponse"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/context:
    post:
      summary: Prompt-ready context with citations
      tags:
        - Retrieval
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SearchPayload"
      responses:
        "200":
          description: Context text and citations
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContextResponse"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/usage/today:
    get:
      summary: Usage counters and effective plan caps for today
      tags:
        - Usage
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Current usage
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/dashboard/bootstrap:
    post:
      summary: "Pre-session workspace bootstrap: validates Supabase access_token, returns existing workspace or creates one
        via create_workspace; client then establishes cookie via POST /v1/dashboard/session."
      tags:
        - Dashboard
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                access_token:
                  type: string
                workspace_name:
                  type: string
              required:
                - access_token
              additionalProperties: false
      responses:
        "200":
          description: Workspace id and name; created indicates new workspace
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardOpsEnvelope"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/dashboard/workspaces:
    get:
      summary: List workspaces for the signed-in dashboard user (membership-scoped)
      tags:
        - Dashboard
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Workspace list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardOpsEnvelope"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    post:
      summary: Create a workspace (CSRF required)
      tags:
        - Dashboard
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
              required:
                - name
              additionalProperties: false
      responses:
        "200":
          description: Created workspace
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardOpsEnvelope"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Invalid CSRF
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/dashboard/api-keys:
    get:
      summary: List API keys for a workspace (query workspace_id optional; must match session workspace)
      tags:
        - Dashboard
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            example: 00000000-0000-4000-8000-000000000000
          required: false
          name: workspace_id
          in: query
      responses:
        "200":
          description: API keys
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardOpsEnvelope"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    post:
      summary: Create API key (CSRF required)
      tags:
        - Dashboard
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                workspace_id:
                  type: string
                  format: uuid
                name:
                  type: string
              required:
                - workspace_id
                - name
              additionalProperties: false
      responses:
        "200":
          description: New key material (once)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardOpsEnvelope"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Invalid CSRF
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/dashboard/api-keys/revoke:
    post:
      summary: Revoke API key (CSRF required)
      tags:
        - Dashboard
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                api_key_id:
                  type: string
                  format: uuid
              required:
                - api_key_id
              additionalProperties: false
      responses:
        "200":
          description: Revocation result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardOpsEnvelope"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Invalid CSRF
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/billing/status:
    get:
      summary: Current billing status and plan
      tags:
        - Billing
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Billing status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingStatusResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/billing/checkout:
    post:
      summary: Create PayU checkout session
      tags:
        - Billing
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BillingCheckoutPayload"
      responses:
        "200":
          description: Checkout URL or form fields
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/dashboard/session:
    post:
      summary: Establish dashboard browser session (cookie + CSRF); implemented in workerApp.ts
      tags:
        - Dashboard
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                access_token:
                  type: string
                workspace_id:
                  type: string
              required:
                - access_token
                - workspace_id
      responses:
        "200":
          description: Session established; sets cookies and returns csrf_token when applicable
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/dashboard/logout:
    post:
      summary: Clear dashboard session cookie
      tags:
        - Dashboard
      responses:
        "200":
          description: Logout acknowledged
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    enum:
                      - true
                required:
                  - ok
        "403":
          description: Invalid CSRF
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/mcp:
    get:
      summary: "Hosted Streamable MCP (also served as /mcp); Authorization: Bearer API key"
      tags:
        - MCP
      security:
        - BearerAuth: []
      responses:
        "200":
          description: MCP capability response (SSE/streamable HTTP)
        "426":
          description: Upgrade required for WebSocket legacy clients
    post:
      summary: Hosted MCP POST (JSON-RPC / streamable)
      tags:
        - MCP
      security:
        - BearerAuth: []
      responses:
        "200":
          description: MCP response
    delete:
      summary: Terminate MCP session when applicable
      tags:
        - MCP
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Session cleared
