openapi: 3.0.3
info:
  title: AgentStudio Core API
  description: |
    AI agent marketplace API by LiminaHub. Publishers create autonomous AI agents,
    users discover, install, configure, and chat with them.

    ## Authentication
    All authenticated endpoints require a JWT token in an httpOnly cookie named `token`.
    The cookie is set automatically by the OAuth sign-in flow.
    For programmatic access, use the `Authorization: Bearer <token>` header.

    ## Response Envelope
    All endpoints return `{ data: T }`. Error responses return `{ error: "message" }`.

    ## Streaming (SSE)
    Chat and Try endpoints use Server-Sent Events. Event types:
    `text`, `tool_start`, `tool_result`, `message_id`, `sandbox_info`, `error`, `done`.

    ## AGNTCY / OASF
    AgentStudio implements the AGNTCY protocol for agent interoperability.
    Directory and discovery endpoints follow OASF (Open Agent Schema Format) v1.0.0.
  version: 1.0.0
  contact:
    name: LiminaHub
    url: https://liminahub.ai
  license:
    name: Proprietary

servers:
  - url: http://localhost:4000
    description: Local development
  - url: https://api.agentstudio.liminahub.ai
    description: Production

tags:
  - name: Auth
    description: OAuth sign-in, session management, sign-out
  - name: Agents
    description: Browse, search, detail, chat, try, execute agents
  - name: Installs
    description: Install, uninstall, manage agent installations
  - name: Runs
    description: Agent execution runs and message history
  - name: Integrations
    description: Integration manifests, credentials, configuration
  - name: Publishers
    description: Publisher profile, agent CRUD, AI generation
  - name: Bundles
    description: Multi-agent solution bundles and pipeline execution
  - name: Background
    description: Cron/webhook/manual background agent execution
  - name: Reviews
    description: Agent ratings and reviews
  - name: Recommendations
    description: Personalized and collaborative-filtering recommendations
  - name: Messages
    description: Message feedback (thumbs up/down)
  - name: Categories
    description: Agent categories
  - name: Stripe
    description: Stripe Connect onboarding, checkout, webhooks
  - name: User Keys
    description: BYOK LLM provider API keys
  - name: Admin
    description: Admin agent review and approval
  - name: Directory
    description: AGNTCY-compatible agent directory (OASF)
  - name: Webhooks
    description: External webhook triggers for background agents
  - name: Cron
    description: Internal cron scheduler
  - name: Discovery
    description: Agent capability-based search and OASF taxonomy
  - name: Reputation
    description: Agent performance scoring, badges, and trending
  - name: Well-Known
    description: AGNTCY instance discovery
  - name: MCP
    description: Model Context Protocol server (StreamableHTTP transport)

# ─── Security Schemes ──────────────────────────────────────────────────────

components:
  securitySchemes:
    cookieAuth:
      type: apiKey
      in: cookie
      name: token
      description: JWT httpOnly cookie set by OAuth sign-in
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    cronSecret:
      type: http
      scheme: bearer
      description: CRON_SECRET environment variable
    webhookSecret:
      type: apiKey
      in: header
      name: x-webhook-secret
      description: Per-install webhook secret

  # ─── Reusable Schemas ──────────────────────────────────────────────────

  schemas:
    # ── Enums ────────────────────────────────────────────────────────────

    AgentStatus:
      type: string
      enum: [DRAFT, SUBMITTED, IN_REVIEW, APPROVED, PUBLISHED, DEPRECATED, REMOVED]

    PricingModel:
      type: string
      enum: [FREE, ONE_TIME, SUBSCRIPTION, USAGE_BASED]

    DeploymentType:
      type: string
      enum: [MANAGED, SELF_HOSTED, MCP_REMOTE, AGNTCY_SLIM]

    InstallStatus:
      type: string
      enum: [ACTIVE, PAUSED, UNINSTALLED]

    RunStatus:
      type: string
      enum: [RUNNING, COMPLETED, FAILED, CANCELLED]

    MessageRole:
      type: string
      enum: [USER, ASSISTANT, TOOL, SYSTEM]

    ForkPolicy:
      type: string
      enum: [ALLOWED, PERSONAL_ONLY, NOT_ALLOWED]

    LlmBillingMode:
      type: string
      enum: [PLATFORM_MANAGED, PUBLISHER_MANAGED, USER_BRINGS_KEY]

    LicenceStatus:
      type: string
      enum: [ACTIVE, PAST_DUE, CANCELLED, EXPIRED]

    PublisherTier:
      type: string
      enum: [FREE, PROFESSIONAL]

    PublisherLevel:
      type: string
      enum: [NEW, ACTIVE, TRUSTED, PARTNER]

    DataFlow:
      type: string
      enum: [SEQUENTIAL, PARALLEL, ORCHESTRATED]

    AuthType:
      type: string
      enum: [OAUTH2, API_KEY, DIRECT_CONNECTION, NONE]

    # ── Core Models ──────────────────────────────────────────────────────

    User:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
          nullable: true
        email:
          type: string
          format: email
        image:
          type: string
          nullable: true

    AgentCard:
      type: object
      description: Compact agent representation for listings
      properties:
        id:
          type: string
        slug:
          type: string
        bundleId:
          type: string
        name:
          type: string
        tagline:
          type: string
        iconUrl:
          type: string
          nullable: true
        categoryName:
          type: string
        categorySlug:
          type: string
        categoryIcon:
          type: string
          nullable: true
        publisherName:
          type: string
        publisherAvatar:
          type: string
          nullable: true
        publisherVerified:
          type: boolean
        pricingModel:
          $ref: '#/components/schemas/PricingModel'
        price:
          type: number
          nullable: true
        avgRating:
          type: number
          format: float
        totalRatings:
          type: integer
        totalInstalls:
          type: integer
        tags:
          type: array
          items:
            type: string
        integrations:
          type: array
          items:
            type: string
        isFeatured:
          type: boolean
        isVerified:
          type: boolean

    AgentDetail:
      allOf:
        - $ref: '#/components/schemas/AgentCard'
        - type: object
          properties:
            description:
              type: string
            goal:
              type: string
            screenshotUrls:
              type: array
              items:
                type: string
            autonomyLevel:
              type: string
            forkPolicy:
              $ref: '#/components/schemas/ForkPolicy'
            successRate:
              type: number
            estimatedCostMin:
              type: number
              nullable: true
            estimatedCostMax:
              type: number
              nullable: true
            llmBillingMode:
              $ref: '#/components/schemas/LlmBillingMode'
            publisherId:
              type: string
            publisherBio:
              type: string
              nullable: true
            publisherLevel:
              $ref: '#/components/schemas/PublisherLevel'
            createdAt:
              type: string
              format: date-time
            publishedAt:
              type: string
              format: date-time
              nullable: true
            latestVersion:
              $ref: '#/components/schemas/AgentVersion'
            versions:
              type: array
              items:
                type: object
                properties:
                  version:
                    type: string
                  changelog:
                    type: string
                  status:
                    type: string
                  createdAt:
                    type: string
                    format: date-time

    AgentVersion:
      type: object
      properties:
        version:
          type: string
        changelog:
          type: string
          nullable: true
        skills:
          type: array
          items:
            type: string
        integrations:
          type: array
          items:
            type: string
        modelPreferences:
          type: object
          description: "JSON — e.g. { preferred_model: 'sonnet' }"
        memoryConfig:
          type: object
          description: "JSON — e.g. { sessionMemory: 'true' }"
        composition:
          type: object
          nullable: true
        configTemplate:
          type: object
          nullable: true
          description: Publisher-defined user config fields

    AgentInstallRecord:
      type: object
      properties:
        id:
          type: string
        agentId:
          type: string
        agentSlug:
          type: string
        agentName:
          type: string
        agentTagline:
          type: string
        agentIconUrl:
          type: string
          nullable: true
        categoryName:
          type: string
        publisherName:
          type: string
        versionInstalled:
          type: string
        status:
          $ref: '#/components/schemas/InstallStatus'
        installedAt:
          type: string
          format: date-time
        hasBackground:
          type: boolean
        backgroundEnabled:
          type: boolean
        backgroundTriggerType:
          type: string
          nullable: true
        backgroundNextRunAt:
          type: string
          format: date-time
          nullable: true
        backgroundLastRunAt:
          type: string
          format: date-time
          nullable: true
        backgroundLastRunStatus:
          type: string
          nullable: true

    RunDetail:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
          nullable: true
        status:
          $ref: '#/components/schemas/RunStatus'
        totalTokens:
          type: integer
        startedAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time
          nullable: true
        messages:
          type: array
          items:
            $ref: '#/components/schemas/AgentMessage'
        install:
          type: object
          properties:
            agent:
              type: object
              properties:
                name:
                  type: string
                slug:
                  type: string
                id:
                  type: string

    RunSummary:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
          nullable: true
        status:
          $ref: '#/components/schemas/RunStatus'
        messageCount:
          type: integer
        totalTokens:
          type: integer
        startedAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time
          nullable: true

    AgentMessage:
      type: object
      properties:
        id:
          type: string
        role:
          $ref: '#/components/schemas/MessageRole'
        content:
          type: string
        toolName:
          type: string
          nullable: true
        toolInput:
          type: object
          nullable: true
        toolResult:
          type: object
          nullable: true
        tokens:
          type: integer
        feedback:
          type: string
          nullable: true
          enum: [positive, negative, null]
        createdAt:
          type: string
          format: date-time

    ChatMessage:
      type: object
      required: [role, content]
      properties:
        role:
          type: string
          enum: [user, assistant]
        content:
          type: string

    IntegrationManifest:
      type: object
      properties:
        id:
          type: string
        slug:
          type: string
        name:
          type: string
        description:
          type: string
          nullable: true
        authType:
          $ref: '#/components/schemas/AuthType'
        requiredScopes:
          type: array
          items:
            type: string
        setupSteps:
          type: array
          items:
            type: string
        configFields:
          type: object
          nullable: true
          description: JSON schema for integration config form fields
        category:
          type: string
          nullable: true

    PublisherProfile:
      type: object
      properties:
        id:
          type: string
        handle:
          type: string
        displayName:
          type: string
        bio:
          type: string
          nullable: true
        avatarUrl:
          type: string
          nullable: true
        websiteUrl:
          type: string
          nullable: true
        githubUrl:
          type: string
          nullable: true
        level:
          $ref: '#/components/schemas/PublisherLevel'
        isVerified:
          type: boolean
        tier:
          $ref: '#/components/schemas/PublisherTier'
        stripeAccountId:
          type: string
          nullable: true
        stripeAccountStatus:
          type: string
          nullable: true

    BundleSummary:
      type: object
      properties:
        id:
          type: string
        slug:
          type: string
        name:
          type: string
        tagline:
          type: string
        dataFlow:
          $ref: '#/components/schemas/DataFlow'
        installMode:
          type: string
        pricingModel:
          $ref: '#/components/schemas/PricingModel'
        price:
          type: number
          nullable: true
        agentCount:
          type: integer
        combinedIntegrations:
          type: array
          items:
            type: string
        agents:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              tagline:
                type: string
              slug:
                type: string
              role:
                type: string
              orderIndex:
                type: integer
        createdAt:
          type: string
          format: date-time

    BundleDetail:
      allOf:
        - $ref: '#/components/schemas/BundleSummary'
        - type: object
          properties:
            description:
              type: string
            goal:
              type: string
            estimatedCostMin:
              type: number
              nullable: true
            estimatedCostMax:
              type: number
              nullable: true
            publisherId:
              type: string

    OASFRecord:
      type: object
      description: OASF v1.0.0 agent descriptor (AGNTCY standard)
      properties:
        name:
          type: string
        version:
          type: string
        schema_version:
          type: string
          example: "1.0.0"
        description:
          type: string
        authors:
          type: array
          items:
            type: string
        created_at:
          type: string
          format: date-time
        skills:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
        domains:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
        modules:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
              data:
                type: object
        annotations:
          type: object
          additionalProperties:
            type: string

    BackgroundSchedule:
      type: object
      properties:
        id:
          type: string
        installId:
          type: string
        enabled:
          type: boolean
        triggerType:
          type: string
          enum: [cron, webhook, manual]
        cronExpression:
          type: string
          nullable: true
        taskPrompt:
          type: string
        webhookSecret:
          type: string
          nullable: true
        nextRunAt:
          type: string
          format: date-time
          nullable: true
        lastRunAt:
          type: string
          format: date-time
          nullable: true
        lastRunStatus:
          type: string
          nullable: true
        maxConcurrentRuns:
          type: integer
        timeoutSeconds:
          type: integer

    Review:
      type: object
      properties:
        id:
          type: string
        rating:
          type: integer
          minimum: 1
          maximum: 5
        title:
          type: string
          nullable: true
        review:
          type: string
          nullable: true
        isVerifiedInstall:
          type: boolean
        userName:
          type: string
        userImage:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time

    UserProviderKey:
      type: object
      properties:
        id:
          type: string
        provider:
          type: string
          enum: [anthropic, openai, huggingface, google, mistral]
        label:
          type: string
          nullable: true
        maskedKey:
          type: string
          description: "e.g. sk-ant-...x7JF"
        isDefault:
          type: boolean
        createdAt:
          type: string
          format: date-time

    Category:
      type: object
      properties:
        id:
          type: string
        slug:
          type: string
        name:
          type: string
        description:
          type: string
          nullable: true
        iconName:
          type: string
          nullable: true
        agentCount:
          type: integer

    SSEEvent:
      type: object
      description: Server-Sent Event payload (one of the types below)
      oneOf:
        - type: object
          properties:
            type:
              type: string
              enum: [text]
            content:
              type: string
        - type: object
          properties:
            type:
              type: string
              enum: [tool_start]
            tool:
              type: string
            input:
              type: object
            toolCallId:
              type: string
        - type: object
          properties:
            type:
              type: string
              enum: [tool_result]
            tool:
              type: string
            result:
              type: string
            toolCallId:
              type: string
        - type: object
          properties:
            type:
              type: string
              enum: [message_id]
            messageId:
              type: string
        - type: object
          properties:
            type:
              type: string
              enum: [sandbox_info]
            remaining:
              type: integer
        - type: object
          properties:
            type:
              type: string
              enum: [error]
            message:
              type: string
        - type: object
          properties:
            type:
              type: string
              enum: [done]

    AgentCapability:
      type: object
      description: Machine-readable capability descriptor for agent discovery
      properties:
        agentId:
          type: string
        slug:
          type: string
        bundleId:
          type: string
        name:
          type: string
        description:
          type: string
        skills:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
                description: OASF taxonomy skill ID
              name:
                type: string
        domains:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
        integrations:
          type: array
          items:
            type: string
        successRate:
          type: number
          description: "0.0-1.0, % of runs completed successfully"
        avgResponseTimeMs:
          type: integer
          nullable: true
        avgTokenCost:
          type: number
          nullable: true
        totalExecutions:
          type: integer
        reputationScore:
          type: number
          description: "Composite 0.0-1.0 score"
        estimatedCostMin:
          type: number
          nullable: true
        estimatedCostMax:
          type: number
          nullable: true
        latencySlaMs:
          type: integer
          nullable: true
        pricingModel:
          $ref: '#/components/schemas/PricingModel'
        supportedProtocols:
          type: array
          items:
            type: string
            enum: [sse, mcp, slim]
        deploymentType:
          $ref: '#/components/schemas/DeploymentType'
        publisherName:
          type: string
        publisherVerified:
          type: boolean
        avgRating:
          type: number
        totalInstalls:
          type: integer

    Error:
      type: object
      properties:
        error:
          type: string
        code:
          type: string

    PaginatedResponse:
      type: object
      properties:
        items:
          type: array
          items: {}
        total:
          type: integer
        page:
          type: integer
        totalPages:
          type: integer

# ─── Paths ───────────────────────────────────────────────────────────────

paths:
  # ── Health ─────────────────────────────────────────────────────────────

  /health:
    get:
      summary: Health check
      tags: [Discovery]
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  service:
                    type: string
                    example: core-api

  # ── Auth ───────────────────────────────────────────────────────────────

  /v1/auth/session:
    get:
      summary: Get current session
      description: Returns the authenticated user from JWT cookie, or null if not signed in.
      tags: [Auth]
      responses:
        '200':
          description: Session info
          content:
            application/json:
              schema:
                type: object
                properties:
                  user:
                    oneOf:
                      - $ref: '#/components/schemas/User'
                      - type: "null"

  /v1/auth/oauth/google:
    get:
      summary: Initiate Google OAuth
      description: Redirects to Google OAuth consent screen. Sets JWT cookie on callback.
      tags: [Auth]
      parameters:
        - name: callbackUrl
          in: query
          schema:
            type: string
          description: URL to redirect after sign-in
        - name: agentId
          in: query
          schema:
            type: string
          description: If provided, auto-creates AgentInstall on callback
      responses:
        '302':
          description: Redirects to Google OAuth

  /v1/auth/oauth/google/callback:
    get:
      summary: Google OAuth callback
      tags: [Auth]
      parameters:
        - name: code
          in: query
          required: true
          schema:
            type: string
        - name: state
          in: query
          required: true
          schema:
            type: string
            description: Base64url encoded JSON with callbackUrl and agentId
      responses:
        '302':
          description: Sets JWT cookie and redirects to callbackUrl

  /v1/auth/oauth/github:
    get:
      summary: Initiate GitHub OAuth
      tags: [Auth]
      parameters:
        - name: callbackUrl
          in: query
          schema:
            type: string
      responses:
        '302':
          description: Redirects to GitHub OAuth

  /v1/auth/oauth/github/callback:
    get:
      summary: GitHub OAuth callback
      tags: [Auth]
      parameters:
        - name: code
          in: query
          required: true
          schema:
            type: string
        - name: state
          in: query
          required: true
          schema:
            type: string
      responses:
        '302':
          description: Sets JWT cookie and redirects to callbackUrl

  /v1/auth/signout:
    post:
      summary: Sign out
      description: Clears the JWT cookie.
      tags: [Auth]
      responses:
        '200':
          description: Signed out
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true

  # ── Agents ─────────────────────────────────────────────────────────────

  /v1/agents:
    get:
      summary: List agents
      description: Browse agents with filtering, sorting, and pagination.
      tags: [Agents]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 12
        - name: category
          in: query
          schema:
            type: string
          description: Filter by category slug
        - name: minRating
          in: query
          schema:
            type: number
        - name: pricingModel
          in: query
          schema:
            $ref: '#/components/schemas/PricingModel'
        - name: verifiedOnly
          in: query
          schema:
            type: boolean
        - name: search
          in: query
          schema:
            type: string
          description: Full-text search on name + description
        - name: sort
          in: query
          schema:
            type: string
            enum: [popular, rating, newest, name]
        - name: integrations
          in: query
          schema:
            type: string
          description: Comma-separated integration slugs (e.g. slack,github)
      responses:
        '200':
          description: Paginated agent list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      items:
                        type: array
                        items:
                          $ref: '#/components/schemas/AgentCard'
                      total:
                        type: integer
                      page:
                        type: integer
                      totalPages:
                        type: integer

  /v1/agents/featured:
    get:
      summary: Get featured agents
      tags: [Agents]
      responses:
        '200':
          description: Featured agents
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentCard'

  /v1/agents/trending:
    get:
      summary: Get trending agents
      tags: [Agents]
      responses:
        '200':
          description: Trending agents
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentCard'

  /v1/agents/by-slug/{slug}:
    get:
      summary: Get agent detail
      description: |
        Returns full agent detail including description, versions, publisher info.
        **Important:** Use `/by-slug/:slug` not `/:slug`.
      tags: [Agents]
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Agent detail
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/AgentDetail'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/agents/search:
    get:
      summary: Search agents (for dependency picker)
      tags: [Agents]
      security:
        - cookieAuth: []
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            minLength: 1
      responses:
        '200':
          description: Matching agents
          content:
            application/json:
              schema:
                type: object
                properties:
                  agents:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        bundleId:
                          type: string
                        publisher:
                          type: object
                          properties:
                            handle:
                              type: string
                            displayName:
                              type: string

  /v1/agents/{slug}/oasf:
    get:
      summary: Export agent as OASF Record
      description: Returns the agent in OASF v1.0.0 format for AGNTCY directory interop.
      tags: [Agents, Directory]
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OASF Record
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OASFRecord'
        '404':
          description: Agent not found

  /v1/agents/import/oasf:
    post:
      summary: Import/preview OASF Record
      description: Parses an OASF Record and returns a preview of the agent that would be created.
      tags: [Agents, Directory]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, version, schema_version, description]
              properties:
                name:
                  type: string
                version:
                  type: string
                schema_version:
                  type: string
                description:
                  type: string
                skills:
                  type: array
                  items:
                    oneOf:
                      - type: string
                      - type: object
                        properties:
                          name:
                            type: string
                modules:
                  type: array
                  items:
                    oneOf:
                      - type: string
                      - type: object
                        properties:
                          name:
                            type: string
                authors:
                  type: array
                  items:
                    type: string
                domains:
                  type: array
                  items:
                    oneOf:
                      - type: string
                      - type: object
                        properties:
                          name:
                            type: string
      responses:
        '200':
          description: Import preview
          content:
            application/json:
              schema:
                type: object
                properties:
                  action:
                    type: string
                    example: preview
                  import:
                    type: object
                    properties:
                      name:
                        type: string
                      slug:
                        type: string
                      description:
                        type: string
                      version:
                        type: string
                      schemaVersion:
                        type: string
                      authors:
                        type: array
                        items:
                          type: string
                      skills:
                        type: array
                        items:
                          type: string
                      integrations:
                        type: array
                        items:
                          type: string
                      domains:
                        type: array
                        items:
                          type: string

  /v1/agents/{agentId}/chat:
    post:
      summary: Chat with agent (SSE streaming)
      description: |
        Sends a message and streams the response via Server-Sent Events.
        The agent has access to configured integrations and tools.

        **SSE Event Types:**
        - `text` — Streamed text content
        - `tool_start` — Agent is calling a tool
        - `tool_result` — Tool execution result
        - `message_id` — DB message ID (for feedback submission)
        - `error` — Error occurred
        - `done` — Stream complete
      tags: [Agents]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message]
              properties:
                message:
                  type: string
                  description: User message to send
                runId:
                  type: string
                  description: Existing run ID to continue conversation
                history:
                  type: array
                  items:
                    $ref: '#/components/schemas/ChatMessage'
                  description: Previous messages for context
      responses:
        '200':
          description: SSE stream
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/SSEEvent'
        '401':
          description: Not authenticated
        '404':
          description: Agent not found or not installed

  /v1/agents/{agentId}/execute:
    post:
      summary: Execute agent (non-streaming)
      description: |
        Runs agent without streaming. Used by webhooks, cron, and background tasks.
        Returns the complete result in one response.
      tags: [Agents]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                message:
                  type: string
                triggerType:
                  type: string
                  enum: [chat, webhook, schedule, manual]
                installId:
                  type: string
                runId:
                  type: string
                history:
                  type: array
                  items:
                    $ref: '#/components/schemas/ChatMessage'
      responses:
        '200':
          description: Execution result
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  response:
                    type: string
                  tokensUsed:
                    type: integer
                  error:
                    type: string
                    nullable: true

  /v1/agents/{agentId}/try:
    post:
      summary: Try agent in sandbox (SSE streaming)
      description: |
        Free sandbox mode — 5 messages per user per agent.
        No integrations or tools available. Agent demonstrates capabilities.
        Requires sign-in (no guest mode).

        Returns same SSE events as `/chat` plus `sandbox_info` with remaining messages.
      tags: [Agents]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message]
              properties:
                message:
                  type: string
                history:
                  type: array
                  items:
                    $ref: '#/components/schemas/ChatMessage'
      responses:
        '200':
          description: SSE stream with sandbox_info events
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/SSEEvent'
        '403':
          description: Sandbox limit reached (5 messages)

  # ── Installs ───────────────────────────────────────────────────────────

  /v1/installs:
    get:
      summary: List installed agents
      tags: [Installs]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: User's active installs
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentInstallRecord'
    post:
      summary: Install an agent
      tags: [Installs]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [agentId]
              properties:
                agentId:
                  type: string
      responses:
        '200':
          description: Install created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/AgentInstallRecord'

  /v1/installs/agent/{agentId}:
    get:
      summary: Get install record for agent
      tags: [Installs]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Install record or null
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    oneOf:
                      - $ref: '#/components/schemas/AgentInstallRecord'
                      - type: "null"

  /v1/installs/status/{agentId}:
    get:
      summary: Check install status
      tags: [Installs]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Install status
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      status:
                        type: string
                        nullable: true

  /v1/installs/{agentId}:
    patch:
      summary: Update install (pause/resume)
      tags: [Installs]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [status]
              properties:
                status:
                  type: string
                  enum: [ACTIVE, PAUSED]
      responses:
        '200':
          description: Updated install
    delete:
      summary: Uninstall agent
      tags: [Installs]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Uninstalled
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean

  # ── Runs ───────────────────────────────────────────────────────────────

  /v1/runs:
    get:
      summary: List recent runs
      tags: [Runs]
      security:
        - cookieAuth: []
      parameters:
        - name: installId
          in: query
          schema:
            type: string
          description: Filter by install ID
      responses:
        '200':
          description: Run list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/RunSummary'
    post:
      summary: Create a new run
      tags: [Runs]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [installId]
              properties:
                installId:
                  type: string
      responses:
        '200':
          description: Run created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      runId:
                        type: string

  /v1/runs/{id}:
    get:
      summary: Get run detail with messages
      tags: [Runs]
      security:
        - cookieAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Run with messages
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/RunDetail'

  /v1/runs/install/{agentId}:
    get:
      summary: Get install for agent (with agent details)
      tags: [Runs]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Install record with agent info

  # ── Integrations ───────────────────────────────────────────────────────

  /v1/integrations:
    get:
      summary: List all integrations
      description: Returns all 15 integration manifests with their config field schemas.
      tags: [Integrations]
      responses:
        '200':
          description: Integration list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/IntegrationManifest'

  /v1/integrations/agent/{agentId}:
    get:
      summary: Get integrations required by agent
      tags: [Integrations]
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Required integrations with configFields

  /v1/integrations/config/{agentId}:
    get:
      summary: Get saved integration config
      tags: [Integrations]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Config values or null
    post:
      summary: Save integration credentials
      description: |
        Saves integration credentials (API keys, tokens) for an installed agent.
        Credentials are stored in AgentInstall.configValues.
      tags: [Integrations]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [configValues]
              properties:
                configValues:
                  type: object
                  description: Key-value pairs matching the integration's configFields schema
                  example:
                    slack_bot_token: "xoxb-..."
                    slack_channel: "#general"
      responses:
        '200':
          description: Config saved
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean

  /v1/integrations/user-config/{agentId}:
    get:
      summary: Get user-specific config
      tags: [Integrations]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: User config or null
    post:
      summary: Save user-specific config
      tags: [Integrations]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [userConfig]
              properties:
                userConfig:
                  type: object
      responses:
        '200':
          description: Saved

  /v1/integrations/validate:
    post:
      summary: Test integration credentials
      description: Validates credentials by making a test API call to the integration.
      tags: [Integrations]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [integrationSlug, configValues]
              properties:
                integrationSlug:
                  type: string
                  description: e.g. "slack", "github", "notion"
                configValues:
                  type: object
                  description: Credentials to test
      responses:
        '200':
          description: Validation result
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid:
                    type: boolean
                  error:
                    type: string

  # ── Publishers ──────────────────────────────────────────────────────────

  /v1/publishers/me:
    get:
      summary: Get my publisher profile
      tags: [Publishers]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Publisher profile or null
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    oneOf:
                      - $ref: '#/components/schemas/PublisherProfile'
                      - type: "null"
    post:
      summary: Become a publisher
      tags: [Publishers]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [displayName]
              properties:
                displayName:
                  type: string
                  minLength: 2
      responses:
        '200':
          description: Publisher created (handle auto-generated)
    patch:
      summary: Update publisher profile
      tags: [Publishers]
      security:
        - cookieAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                displayName:
                  type: string
                bio:
                  type: string
                websiteUrl:
                  type: string
                githubUrl:
                  type: string
                llmApiKey:
                  type: string
                  description: Publisher's LLM API key (encrypted at rest)
      responses:
        '200':
          description: Updated profile

  /v1/publishers/me/agents:
    get:
      summary: List my agents
      tags: [Publishers]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Publisher's agents

  /v1/publishers/me/stats:
    get:
      summary: Get publisher stats
      tags: [Publishers]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Stats
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      totalAgents:
                        type: integer
                      totalInstalls:
                        type: integer
                      avgRating:
                        type: number
                      totalRatings:
                        type: integer
                      level:
                        type: string
                      isVerified:
                        type: boolean

  /v1/publishers/me/earnings:
    get:
      summary: Get publisher earnings
      tags: [Publishers]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Earnings breakdown
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      lifetimeRevenue:
                        type: number
                      trailingMonthRevenue:
                        type: number
                      pendingBalanceCents:
                        type: number
                      commissionRate:
                        type: number
                        description: "0.15 (15%) or 0.10 (10% for VIP)"
                      tier:
                        type: string
                      stripeAccountStatus:
                        type: string

  /v1/publishers/agents:
    post:
      summary: Create a new agent
      description: |
        Creates a new agent with all configuration. The agent starts in DRAFT status.
        Submit for review with `/submit` endpoint after creation.
      tags: [Publishers]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, slug, tagline, categoryId, description, goal]
              properties:
                name:
                  type: string
                slug:
                  type: string
                  description: URL-safe identifier (unique per store)
                tagline:
                  type: string
                  maxLength: 120
                categoryId:
                  type: string
                description:
                  type: string
                goal:
                  type: string
                tags:
                  oneOf:
                    - type: string
                      description: Comma-separated
                    - type: array
                      items:
                        type: string
                pricingModel:
                  $ref: '#/components/schemas/PricingModel'
                price:
                  type: number
                autonomyLevel:
                  type: string
                  default: L2_supervised
                forkPolicy:
                  $ref: '#/components/schemas/ForkPolicy'
                version:
                  type: string
                  default: "1.0.0"
                changelog:
                  type: string
                integrations:
                  type: array
                  items:
                    type: string
                  description: Integration slugs (e.g. ["slack", "github"])
                skills:
                  type: array
                  items:
                    type: string
                customInstructions:
                  type: string
                  description: System prompt / custom instructions
                personality:
                  type: string
                saveDraft:
                  type: boolean
                deploymentType:
                  $ref: '#/components/schemas/DeploymentType'
                endpointUrl:
                  type: string
                  description: Required for SELF_HOSTED / MCP_REMOTE
                endpointAuthType:
                  type: string
                preferredModel:
                  type: string
                  description: "Model alias: sonnet, haiku, opus, deepseek, gpt-4o, etc."
                dependencies:
                  type: array
                  items:
                    type: string
                  description: Bundle IDs of dependent agents
                backgroundEnabled:
                  type: boolean
                backgroundTriggerType:
                  type: string
                  enum: [cron, webhook, manual]
                backgroundCron:
                  type: string
                  description: Cron expression (e.g. "0 9 * * 1-5")
                backgroundTaskPrompt:
                  type: string
                backgroundTimeout:
                  type: integer
                configFields:
                  type: array
                  items:
                    type: object
                  description: User-configurable fields schema
      responses:
        '200':
          description: Agent created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  slug:
                    type: string

  /v1/publishers/agents/{agentId}:
    get:
      summary: Get agent for editing
      tags: [Publishers]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Full agent with versions and dependencies
    put:
      summary: Update an agent
      description: Same fields as create. Updates the latest version.
      tags: [Publishers]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: Same fields as POST /publishers/agents
      responses:
        '200':
          description: Agent updated

  /v1/publishers/agents/{agentId}/submit:
    post:
      summary: Submit agent for review
      description: Changes status from DRAFT to SUBMITTED.
      tags: [Publishers]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Submitted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean

  /v1/publishers/agents/{agentId}/endpoint-secret:
    post:
      summary: Generate HMAC signing secret
      description: Creates a new HMAC-SHA256 secret for external agent endpoints.
      tags: [Publishers]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Secret generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      secret:
                        type: string
                        description: 32-byte hex string (show once, then hashed)
                      algorithm:
                        type: string
                        example: hmac_sha256
                      message:
                        type: string
    delete:
      summary: Revoke signing secret
      tags: [Publishers]
      security:
        - cookieAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Secret revoked

  /v1/publishers/generate-agent:
    post:
      summary: AI-powered agent generation
      description: |
        Describe what you want in 2-3 sentences. AI generates full agent spec:
        name, slug, tagline, description, goal, tags, integrations, skills,
        system prompt, config fields, autonomy level, pricing, background config.
      tags: [Publishers]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [prompt]
              properties:
                prompt:
                  type: string
                  minLength: 10
                  description: Natural language description of the agent
                  example: "I want an agent that monitors my Gmail for invoices, extracts the amounts, and logs them in a Google Sheet"
      responses:
        '200':
          description: Generated agent specification (JSON matching create agent schema)

  /v1/publishers/agent-chat:
    post:
      summary: Conversational agent creation via SSE
      description: |
        Multi-turn conversational endpoint for creating agents. Send the full
        message history each call. The LLM streams back text tokens as SSE
        events. When enough context has been gathered (typically 3-5 turns),
        the response includes an `agent_spec` SSE event with the validated
        JSON specification.

        SSE event types:
        - `text`        — streaming token: `{ type: "text", content: "..." }`
        - `agent_spec`  — validated spec: `{ type: "agent_spec", agentSpec: {...}, isComplete: true }`
        - `error`       — error: `{ type: "error", error: "..." }`
        - `done`        — stream finished: `{ type: "done" }`
      tags: [Publishers]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [messages]
              properties:
                messages:
                  type: array
                  items:
                    type: object
                    required: [role, content]
                    properties:
                      role:
                        type: string
                        enum: [user, assistant]
                      content:
                        type: string
                  description: Full conversation history (user and assistant turns)
                  example:
                    - role: user
                      content: "I want an agent that monitors Slack for customer complaints"
      responses:
        '200':
          description: SSE stream of text tokens and optional agent_spec
          content:
            text/event-stream:
              schema:
                type: string
        '400':
          description: Missing or invalid messages array
        '403':
          description: Not a publisher

  /v1/publishers/agents/import:
    post:
      summary: Import external agent
      tags: [Publishers]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Agent imported

  /v1/publishers/validate-endpoint:
    post:
      summary: Validate external endpoint URL
      tags: [Publishers]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  format: uri
                authType:
                  type: string
      responses:
        '200':
          description: Validation result
          content:
            application/json:
              schema:
                type: object
                properties:
                  isValid:
                    type: boolean
                  error:
                    type: string

  /v1/publishers/{id}:
    get:
      summary: Public publisher profile
      tags: [Publishers]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Publisher profile

  # ── Bundles ────────────────────────────────────────────────────────────

  /v1/bundles:
    get:
      summary: List solution bundles
      tags: [Bundles]
      responses:
        '200':
          description: Bundle list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/BundleSummary'
    post:
      summary: Create a bundle
      tags: [Bundles]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, tagline, description, goal, agents]
              properties:
                name:
                  type: string
                tagline:
                  type: string
                description:
                  type: string
                goal:
                  type: string
                dataFlow:
                  $ref: '#/components/schemas/DataFlow'
                installMode:
                  type: string
                  enum: [ALL_OR_NOTHING, MODULAR]
                agents:
                  type: array
                  minItems: 2
                  items:
                    type: object
                    required: [agentId, role]
                    properties:
                      agentId:
                        type: string
                      role:
                        type: string
      responses:
        '200':
          description: Bundle created

  /v1/bundles/by-slug/{slug}:
    get:
      summary: Get bundle detail
      tags: [Bundles]
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Bundle detail
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/BundleDetail'

  /v1/bundles/{bundleId}/install:
    post:
      summary: Install all agents in bundle
      tags: [Bundles]
      security:
        - cookieAuth: []
      parameters:
        - name: bundleId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Install results per agent
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        agentId:
                          type: string
                        agentName:
                          type: string
                        installed:
                          type: boolean

  /v1/bundles/{bundleId}/execute:
    post:
      summary: Execute bundle pipeline
      description: Runs agents sequentially, passing each output as context to the next.
      tags: [Bundles]
      security:
        - cookieAuth: []
      parameters:
        - name: bundleId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [initialMessage]
              properties:
                initialMessage:
                  type: string
      responses:
        '200':
          description: Pipeline result

  # ── Background ─────────────────────────────────────────────────────────

  /v1/background/{installId}:
    get:
      summary: Get background schedule
      tags: [Background]
      security:
        - cookieAuth: []
      parameters:
        - name: installId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Schedule
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/BackgroundSchedule'
    put:
      summary: Create or update background schedule
      tags: [Background]
      security:
        - cookieAuth: []
      parameters:
        - name: installId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                triggerType:
                  type: string
                  enum: [cron, webhook, manual]
                cronExpression:
                  type: string
                  description: "e.g. '0 9 * * 1-5' (weekdays at 9am)"
                taskPrompt:
                  type: string
                  description: The prompt executed each run. Supports {{configField}} substitution.
                timeoutSeconds:
                  type: integer
                  default: 300
                maxConcurrentRuns:
                  type: integer
                  default: 1
      responses:
        '200':
          description: Schedule saved

  /v1/background/{installId}/toggle:
    patch:
      summary: Enable/disable schedule
      tags: [Background]
      security:
        - cookieAuth: []
      parameters:
        - name: installId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [enabled]
              properties:
                enabled:
                  type: boolean
      responses:
        '200':
          description: Toggled

  /v1/background/{installId}/runs:
    get:
      summary: Get background run history
      tags: [Background]
      security:
        - cookieAuth: []
      parameters:
        - name: installId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Run history
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        triggerType:
                          type: string
                        taskPrompt:
                          type: string
                        status:
                          $ref: '#/components/schemas/RunStatus'
                        totalTokens:
                          type: integer
                        startedAt:
                          type: string
                          format: date-time
                        completedAt:
                          type: string
                          format: date-time
                          nullable: true

  /v1/background/{installId}/run:
    post:
      summary: Manually trigger background run
      tags: [Background]
      security:
        - cookieAuth: []
      parameters:
        - name: installId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Run triggered
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      runId:
                        type: string
                      status:
                        type: string
                      error:
                        type: string
                        nullable: true

  # ── Reviews ────────────────────────────────────────────────────────────

  /v1/reviews/{agentId}:
    get:
      summary: Get reviews for agent
      tags: [Reviews]
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Review list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Review'

  /v1/reviews:
    post:
      summary: Submit or update review
      description: One review per user per agent. Submitting again updates the existing review.
      tags: [Reviews]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [agentId, rating]
              properties:
                agentId:
                  type: string
                rating:
                  type: integer
                  minimum: 1
                  maximum: 5
                title:
                  type: string
                review:
                  type: string
      responses:
        '200':
          description: Review saved

  # ── Messages ───────────────────────────────────────────────────────────

  /v1/messages/{messageId}/feedback:
    post:
      summary: Submit message feedback
      description: Thumbs up/down on assistant messages. Negative feedback auto-saved to agent memory for self-improvement.
      tags: [Messages]
      security:
        - cookieAuth: []
      parameters:
        - name: messageId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [feedback]
              properties:
                feedback:
                  type: string
                  enum: [positive, negative]
                  nullable: true
                  description: "null to remove feedback"
      responses:
        '200':
          description: Feedback saved
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  feedback:
                    type: string
                    nullable: true

  # ── Recommendations ────────────────────────────────────────────────────

  /v1/recommendations:
    get:
      summary: Personalized recommendations
      description: Returns agents based on user's installed categories, integrations, and popularity.
      tags: [Recommendations]
      security:
        - cookieAuth: []
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 6
      responses:
        '200':
          description: Recommended agents
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentCard'

  /v1/recommendations/similar/{agentId}:
    get:
      summary: Similar agents (collaborative filtering)
      description: "Users who installed X also installed Y"
      tags: [Recommendations]
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 4
      responses:
        '200':
          description: Similar agents
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentCard'

  # ── Categories ─────────────────────────────────────────────────────────

  /v1/categories:
    get:
      summary: List all categories
      tags: [Categories]
      responses:
        '200':
          description: Category list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Category'

  # ── Stripe ─────────────────────────────────────────────────────────────

  /v1/stripe/connect/onboard:
    post:
      summary: Start Stripe Connect onboarding
      description: Creates a Stripe Express account and returns the onboarding URL.
      tags: [Stripe]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Onboarding URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      url:
                        type: string
                        format: uri

  /v1/stripe/connect/callback:
    get:
      summary: Stripe Connect callback
      description: Verifies Stripe account status after onboarding.
      tags: [Stripe]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Account status
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      status:
                        type: string
                        enum: [active, pending]

  /v1/stripe/checkout:
    post:
      summary: Create checkout session
      description: Creates a Stripe Checkout session for a paid agent (subscription or one-time).
      tags: [Stripe]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [agentId]
              properties:
                agentId:
                  type: string
      responses:
        '200':
          description: Checkout URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      checkoutUrl:
                        type: string
                        format: uri

  /v1/stripe/webhooks:
    post:
      summary: Stripe webhook handler
      description: |
        Receives Stripe lifecycle events. Raw body with signature verification.
        Handles: checkout.session.completed, invoice.payment_succeeded,
        invoice.payment_failed, customer.subscription.deleted
      tags: [Stripe]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Event received
          content:
            application/json:
              schema:
                type: object
                properties:
                  received:
                    type: boolean

  # ── User Keys ──────────────────────────────────────────────────────────

  /v1/user/keys:
    get:
      summary: List saved LLM provider keys
      description: Returns keys with masked values (e.g. "sk-ant-...x7JF")
      tags: [User Keys]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Key list
          content:
            application/json:
              schema:
                type: object
                properties:
                  keys:
                    type: array
                    items:
                      $ref: '#/components/schemas/UserProviderKey'
    post:
      summary: Save a new provider key
      description: Key is encrypted with AES-256-GCM before storage.
      tags: [User Keys]
      security:
        - cookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [provider, apiKey]
              properties:
                provider:
                  type: string
                  enum: [anthropic, openai, huggingface, google, mistral]
                apiKey:
                  type: string
                  description: Raw API key (encrypted before storage)
                label:
                  type: string
                  description: User-friendly label
      responses:
        '200':
          description: Key saved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserProviderKey'

  /v1/user/keys/{id}:
    delete:
      summary: Delete a saved key
      tags: [User Keys]
      security:
        - cookieAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Key deleted

  /v1/user/keys/{id}/test:
    post:
      summary: Test a saved key
      description: Makes a tiny LLM call to verify the key works.
      tags: [User Keys]
      security:
        - cookieAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Test result
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  response:
                    type: string
                  error:
                    type: string

  # ── Admin ──────────────────────────────────────────────────────────────

  /v1/admin/agents:
    get:
      summary: List agents for review
      tags: [Admin]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: All agents with full details

  /v1/admin/agents/{id}/status:
    patch:
      summary: Approve, publish, or reject agent
      tags: [Admin]
      security:
        - cookieAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [action]
              properties:
                action:
                  type: string
                  enum: [approve, publish, reject]
      responses:
        '200':
          description: Status updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  action:
                    type: string

  # ── Directory (AGNTCY) ─────────────────────────────────────────────────

  /v1/directory:
    get:
      summary: AGNTCY-compatible directory listing
      description: |
        Public OASF directory API. Returns agents as OASF Records for
        interoperability with the AGNTCY ecosystem.
      tags: [Directory]
      parameters:
        - name: q
          in: query
          schema:
            type: string
          description: Search by name or description
        - name: domain
          in: query
          schema:
            type: string
          description: Filter by domain/category
        - name: skill
          in: query
          schema:
            type: string
          description: Filter by skill name
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Paginated OASF records
          content:
            application/json:
              schema:
                type: object
                properties:
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/OASFRecord'

  /v1/directory/{slug}:
    get:
      summary: Single agent OASF descriptor
      tags: [Directory]
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OASF Record
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OASFRecord'
        '404':
          description: Agent not found

  # ── Well-Known ─────────────────────────────────────────────────────────

  /.well-known/agntcy.json:
    get:
      summary: AGNTCY instance discovery
      description: |
        Returns metadata about this AgentStudio instance for AGNTCY network discovery.
        Other AGNTCY-compatible services use this to find our directory endpoint.
      tags: [Discovery]
      responses:
        '200':
          description: Discovery document
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    example: AgentStudio by LiminaHub
                  type:
                    type: string
                    example: marketplace
                  version:
                    type: string
                    example: "1.0.0"
                  directory_endpoint:
                    type: string
                    example: /v1/directory
                  oasf_schema_version:
                    type: string
                    example: "1.0.0"
                  supported_protocols:
                    type: array
                    items:
                      type: string
                    example: [oasf, http, sse, slim, mcp]
                  mcp_endpoint:
                    type: string
                    example: /mcp
                  agent_count:
                    type: integer

  # ── MCP Server (Model Context Protocol) ────────────────────────────────

  /mcp:
    post:
      summary: MCP JSON-RPC endpoint
      description: |
        StreamableHTTP transport for the Model Context Protocol.
        Send JSON-RPC requests (initialize, tools/list, tools/call, etc.).
        First request must be an `initialize` call; the response sets the
        `Mcp-Session-Id` header for subsequent requests.
      tags: [MCP]
      security:
        - bearerAuth: []
      parameters:
        - name: Mcp-Session-Id
          in: header
          required: false
          schema:
            type: string
          description: Session ID returned from initialize (required after first request)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                jsonrpc:
                  type: string
                  example: "2.0"
                method:
                  type: string
                  example: initialize
                params:
                  type: object
                id:
                  oneOf:
                    - type: string
                    - type: integer
              required: [jsonrpc, method, id]
      responses:
        '200':
          description: JSON-RPC response
          headers:
            Mcp-Session-Id:
              schema:
                type: string
              description: Session ID (set on initialize)
          content:
            application/json:
              schema:
                type: object
                properties:
                  jsonrpc:
                    type: string
                  result:
                    type: object
                  id:
                    oneOf:
                      - type: string
                      - type: integer
        '401':
          description: Invalid or missing API key
        '404':
          description: Session not found
    get:
      summary: MCP SSE stream
      description: |
        Server-Sent Events stream for server-initiated MCP messages.
        Requires an active session via `Mcp-Session-Id` header.
      tags: [MCP]
      parameters:
        - name: Mcp-Session-Id
          in: header
          required: true
          schema:
            type: string
      responses:
        '200':
          description: SSE event stream
          content:
            text/event-stream: {}
        '404':
          description: Session not found
    delete:
      summary: Terminate MCP session
      description: Clean up server resources for an MCP session.
      tags: [MCP]
      parameters:
        - name: Mcp-Session-Id
          in: header
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Session terminated
        '404':
          description: Session not found

  # ── Webhooks ───────────────────────────────────────────────────────────

  /v1/webhooks/{installId}:
    post:
      summary: External webhook trigger
      description: |
        Triggers a background agent run from an external system.
        Validates webhook secret from header or query param.
      tags: [Webhooks]
      security:
        - webhookSecret: []
      parameters:
        - name: installId
          in: path
          required: true
          schema:
            type: string
        - name: secret
          in: query
          schema:
            type: string
          description: Alternative to x-webhook-secret header
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                taskPrompt:
                  type: string
                  description: Override the scheduled task prompt
                payload:
                  type: object
                  description: Webhook payload appended to task prompt as context
      responses:
        '200':
          description: Run result
          content:
            application/json:
              schema:
                type: object
                properties:
                  runId:
                    type: string
                  status:
                    type: string
                  tokensUsed:
                    type: integer
                  response:
                    type: string
                    description: First 5000 chars of response
                  error:
                    type: string
                    nullable: true
        '401':
          description: Invalid webhook secret
        '429':
          description: Max concurrent runs reached

  # ── Discovery ───────────────────────────────────────────────────────────

  /v1/discovery/search:
    get:
      summary: Structured capability search
      description: |
        Find agents by capability, domain, cost, latency, reputation, protocol, and more.
        Returns structured capability descriptors with OASF-mapped skills and domains.
        Designed for orchestrators, CI/CD pipelines, and external systems to programmatically discover agents.
      tags: [Discovery]
      parameters:
        - name: skill
          in: query
          schema:
            type: string
          description: OASF skill name or ID (e.g. "summarization", "workflow_automation")
        - name: domain
          in: query
          schema:
            type: string
          description: Domain/category filter (e.g. "finance", "marketing")
        - name: integration
          in: query
          schema:
            type: string
          description: Required integration (e.g. "slack", "github")
        - name: maxCost
          in: query
          schema:
            type: number
          description: Max estimated cost per execution
        - name: maxLatency
          in: query
          schema:
            type: integer
          description: Max response time in ms
        - name: minReputation
          in: query
          schema:
            type: number
          description: Minimum reputation score (0.0-1.0)
        - name: minRating
          in: query
          schema:
            type: number
          description: Minimum average rating
        - name: pricingModel
          in: query
          schema:
            $ref: '#/components/schemas/PricingModel'
        - name: protocol
          in: query
          schema:
            type: string
            enum: [sse, mcp, slim]
          description: Required protocol support
        - name: deploymentType
          in: query
          schema:
            $ref: '#/components/schemas/DeploymentType'
        - name: q
          in: query
          schema:
            type: string
          description: Text search on name + description + tagline
        - name: sort
          in: query
          schema:
            type: string
            enum: [popular, rating, reputation, newest, cost]
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Matching agents with capability descriptors
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      items:
                        type: array
                        items:
                          $ref: '#/components/schemas/AgentCapability'
                      total:
                        type: integer
                      limit:
                        type: integer
                      offset:
                        type: integer
                      filters:
                        type: object
    post:
      summary: Semantic natural language search
      description: |
        Search for agents using natural language. The system extracts skills and domains
        from the query, scores agents by relevance, and returns ranked results.
        Example: "I need an agent that can analyze quarterly earnings reports"
      tags: [Discovery]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [query]
              properties:
                query:
                  type: string
                  description: Natural language capability search
                  example: "I need an agent to process invoices from email with OCR"
                limit:
                  type: integer
                  default: 10
                  maximum: 50
      responses:
        '200':
          description: Ranked agent matches
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      query:
                        type: string
                      matchedSkills:
                        type: array
                        items:
                          type: string
                      matchedDomains:
                        type: array
                        items:
                          type: string
                      items:
                        type: array
                        items:
                          allOf:
                            - $ref: '#/components/schemas/AgentCapability'
                            - type: object
                              properties:
                                relevanceScore:
                                  type: number
                      total:
                        type: integer

  /v1/discovery/skills:
    get:
      summary: List OASF skill taxonomy
      description: Returns all 121 OASF skills with IDs, names, and captions.
      tags: [Discovery]
      responses:
        '200':
          description: Skill taxonomy
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      total:
                        type: integer
                      skills:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            caption:
                              type: string

  /v1/discovery/domains:
    get:
      summary: List OASF domain taxonomy
      description: Returns all 15 OASF domains with IDs, names, and captions.
      tags: [Discovery]
      responses:
        '200':
          description: Domain taxonomy

  /v1/discovery/{slug}/capabilities:
    get:
      summary: Full capability descriptor for an agent
      description: |
        Returns a machine-readable capability descriptor with OASF-mapped skills,
        domains, performance metrics, cost/SLA, and protocol support.
      tags: [Discovery]
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Agent capability descriptor
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/AgentCapability'
        '404':
          description: Agent not found

  # ── Reputation ─────────────────────────────────────────────────────────

  /v1/reputation/{agentId}:
    get:
      summary: Full reputation breakdown
      description: Returns detailed performance metrics and reputation score for an agent.
      tags: [Reputation]
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Reputation metrics

  /v1/reputation/{agentId}/badges:
    get:
      summary: Agent reputation badges
      description: |
        Returns earned badges based on performance thresholds.
        Badges: fast_responder, high_accuracy, cost_efficient, top_rated, trending.
      tags: [Reputation]
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Badge list

  /v1/store/trending:
    get:
      summary: Reputation-weighted trending agents
      description: |
        Returns trending agents scored by reputation (40%), recent installs (30%),
        executions (20%), and rating (10%). Better than install-count-only trending.
      tags: [Reputation]
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 12
      responses:
        '200':
          description: Trending agents

  /v1/reputation/refresh:
    post:
      summary: Recompute all agent reputations
      description: Admin endpoint to batch-update reputation scores for all published agents.
      tags: [Reputation]
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Reputations refreshed

  # ── Cron ───────────────────────────────────────────────────────────────

  /v1/cron/agent-scheduler:
    post:
      summary: Process due background schedules
      description: |
        Internal endpoint called by cron job. Processes all schedules
        where nextRunAt <= now and enabled = true.
      tags: [Cron]
      security:
        - cronSecret: []
      responses:
        '200':
          description: Schedules processed
