openapi: 3.1.0
info:
  title: Tetrees EX API — Supported Integration Token Contract
  version: 1.0.0
  description: |
    Public discovery, entitled delivery, and owner-scoped seller-prep routes
    supported by ordinary txk_ API tokens. Token creation/revocation, product
    uploads, listing mutations, submit/publish, billing, and license management
    require an interactive browser session and are intentionally excluded.
servers:
  - url: https://ex.tetrees.ai
security:
  - bearerAuth: []
tags:
  - name: Authentication
  - name: Discovery
  - name: Entitled delivery
  - name: Seller owner workflow
paths:
  /api/auth/api-token-status:
    get:
      tags: [Authentication]
      summary: Validate an API token and inspect its effective permissions
      responses:
        '200':
          description: Authenticated token capability
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/catalog/products:
    get:
      tags: [Discovery]
      summary: Browse the sellable public catalog
      parameters:
        - { name: q, in: query, schema: { type: string } }
        - { name: category, in: query, schema: { type: string } }
        - { name: type, in: query, schema: { type: string } }
        - { name: language, in: query, schema: { type: string } }
        - { name: framework, in: query, schema: { type: string } }
        - { name: database, in: query, schema: { type: string } }
        - { name: payment_gateway, in: query, schema: { type: string } }
        - { name: region, in: query, schema: { type: string, enum: [korea] } }
        - { name: tool, in: query, schema: { type: string } }
        - { name: license, in: query, schema: { type: string, enum: [regular, extended, agency, enterprise] } }
        - { name: min_price, in: query, schema: { type: number, minimum: 0 } }
        - { name: max_price, in: query, schema: { type: number, minimum: 0 } }
        - { name: free, in: query, schema: { type: boolean } }
        - { name: verified, in: query, schema: { type: boolean } }
        - { name: team_reviewed, in: query, schema: { type: boolean } }
        - { name: seller, in: query, schema: { type: string, format: uuid } }
        - { name: sort, in: query, schema: { type: string, enum: [newest, best_selling, top_rated, price_low, price_high] } }
        - { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } }
        - { name: page_size, in: query, schema: { type: integer, minimum: 1, maximum: 60, default: 24 } }
      responses:
        '200':
          description: Product cards and pagination
          content:
            application/json:
              schema:
                type: object
                required: [products, pagination]
                properties:
                  products: { type: array, items: { type: object, additionalProperties: true } }
                  pagination: { $ref: '#/components/schemas/Pagination' }
  /api/catalog/products/{slug}:
    get:
      tags: [Discovery]
      summary: Read product detail
      parameters:
        - $ref: '#/components/parameters/ProductSlug'
      responses:
        '200': { description: Product detail, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/catalog/smart-search:
    post:
      tags: [Discovery]
      summary: Find products from a natural-language need
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [query]
              properties:
                query: { type: string, minLength: 3, maxLength: 300 }
      responses:
        '200': { description: Interpreted query and matching products }
        '400': { $ref: '#/components/responses/BadRequest' }
  /api/orders/purchases:
    get:
      tags: [Entitled delivery]
      summary: List purchases available to the token account
      responses:
        '200': { description: Purchase list }
        '401': { $ref: '#/components/responses/Unauthorized' }
  /api/install/products/{productId}/deploy-targets:
    get:
      tags: [Entitled delivery]
      summary: Get the target-specific deployment contract
      parameters:
        - $ref: '#/components/parameters/ProductId'
      responses:
        '200': { description: Deployment targets and entitled actions }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/install/products/{productId}/download:
    get:
      tags: [Entitled delivery]
      summary: Issue a five-minute entitlement-checked download URL
      parameters:
        - $ref: '#/components/parameters/ProductId'
      responses:
        '200':
          description: Short-lived download contract
          content:
            application/json:
              schema:
                type: object
                required: [contractVersion, productId, version, filename, downloadUrl]
                properties:
                  contractVersion: { type: integer, const: 1 }
                  productId: { type: string, format: uuid }
                  version: { type: string, pattern: '^\\d+\\.\\d+\\.\\d+$' }
                  filename: { type: string }
                  downloadUrl: { type: string, format: uri }
                additionalProperties: true
        '403': { $ref: '#/components/responses/Forbidden' }
  /api/seller/products:
    get:
      tags: [Seller owner workflow]
      summary: List products owned by the token account
      responses:
        '200':
          description: Owner-scoped products
          content:
            application/json:
              schema:
                type: object
                required: [products]
                properties:
                  products: { type: array, items: { type: object, additionalProperties: true } }
  /api/seller/products/{productId}:
    get:
      tags: [Seller owner workflow]
      summary: Read one owned product
      parameters:
        - $ref: '#/components/parameters/ProductId'
      responses:
        '200': { description: Owned product detail }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/seller/products/{productId}/prep-quote:
    get:
      tags: [Seller owner workflow]
      summary: Quote all four prep tiers for the current version
      parameters:
        - $ref: '#/components/parameters/ProductId'
      responses:
        '200':
          description: Version- and artifact-size-bound tier quote
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PrepQuote'
        '404': { $ref: '#/components/responses/NotFound' }
  /api/seller/products/{productId}/submission-readiness:
    get:
      tags: [Seller owner workflow]
      summary: Check required listing metadata, code and preview images
      description: MCP and ordinary API tokens may read this contract. ZIP/image transfer and listing edits continue in the signed-in browser URL returned by the response.
      parameters:
        - $ref: '#/components/parameters/ProductId'
      responses:
        '200':
          description: Deterministic required/recommended checklist and browser continuation actions
          content:
            application/json:
              schema:
                type: object
                required: [ready, required, missing, browserActions]
                properties:
                  ready: { type: boolean }
                  required: { type: array, items: { type: object, additionalProperties: true } }
                  missing: { type: array, items: { type: object, additionalProperties: true } }
                  browserActions: { type: object, additionalProperties: { type: string } }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/seller/products/{productId}/prep:
    post:
      tags: [Seller owner workflow]
      summary: Run one explicitly confirmed quoted prep tier
      description: This operation spends Tetrees Points. Quote immediately before calling it.
      parameters:
        - $ref: '#/components/parameters/ProductId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [tier, expectedVersion, expectedPoints, confirmation]
              properties:
                tier: { type: string, enum: [verified_listing, pro_prep, ux_prep, max_prep] }
                expectedVersion: { type: string, pattern: '^\\d+\\.\\d+\\.\\d+$' }
                expectedPoints: { type: integer, minimum: 1 }
                confirmation: { type: string, const: RUN_SELLER_PREP }
      responses:
        '200': { description: Prep job and result }
        '400': { $ref: '#/components/responses/BadRequest' }
        '402': { description: Insufficient points, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } } }
        '409': { description: Version, quote, price, or state changed, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } } }
  /api/seller/products/{productId}/report:
    get:
      tags: [Seller owner workflow]
      summary: Read normalized private AVCP feedback for an owned product
      parameters:
        - $ref: '#/components/parameters/ProductId'
      responses:
        '200': { description: Private seller report }
        '404': { $ref: '#/components/responses/NotFound' }
  /api/seller/connect/status:
    get:
      tags: [Seller owner workflow]
      summary: Read Stripe Connect readiness
      responses:
        '200': { description: Connect status }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: txk_
      description: Create in Account > API access. The plaintext token is shown once.
  parameters:
    ProductId:
      name: productId
      in: path
      required: true
      schema: { type: string, format: uuid }
    ProductSlug:
      name: slug
      in: path
      required: true
      schema: { type: string }
  schemas:
    TokenStatus:
      type: object
      required: [authenticated, principal, accountType, capability, permissions]
      properties:
        authenticated: { type: boolean, const: true }
        principal: { type: string, const: api_token }
        accountType: { type: string }
        capability: { type: string }
        permissions: { type: array, items: { type: string } }
    Pagination:
      type: object
      required: [page, pageSize, total, totalPages]
      properties:
        page: { type: integer }
        pageSize: { type: integer }
        total: { type: integer }
        totalPages: { type: integer }
    PrepQuote:
      type: object
      required: [currentVersion, requiredTier, sizeBytes, tiers, options]
      properties:
        currentVersion: { type: [string, 'null'] }
        requiredTier: { type: string, const: verified_listing }
        sizeBytes: { type: integer, minimum: 0 }
        tiers:
          type: object
          required: [verified_listing, pro_prep, ux_prep, max_prep]
          properties:
            verified_listing: { type: integer }
            pro_prep: { type: integer }
            ux_prep: { type: integer }
            max_prep: { type: integer }
        options: { type: array, items: { type: object, additionalProperties: true } }
    ErrorEnvelope:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code: { type: string }
            message: { type: string }
        authentication: { type: object, additionalProperties: true }
  responses:
    BadRequest:
      description: Invalid parameters or request body
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    Unauthorized:
      description: Missing, invalid, expired, or revoked token
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    Forbidden:
      description: Authenticated but outside scope or resource entitlement
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
    NotFound:
      description: Route or owner-scoped resource not found
      content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } }
