openapi: "3.1.0"
info:
  title: Kamera-ichi 器材市集 API
  version: "1.0.0"
  description: 台灣二手相機、鏡頭與攝影器材交易平台公開 API 與開發者介面。支援商品搜尋、價格行情統計、分類品牌查詢、會員刊登與 MCP (Model Context Protocol) 整合。
  contact:
    name: Kamera-ichi 開發者支援
    email: contact@kamera-ichi.com
    url: https://kamera-ichi.com/developers
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
servers:
  - url: https://kamera-ichi.com/api
    description: Production API Gateway (Cloudflare Pages Proxy)
  - url: https://kamera-ichi.com/api/v1
    description: Versioned production API (v1)
  - url: https://kamera-ichi.com/api/sandbox/v1
    description: Free public sandbox environment (no authentication required)
  - url: https://kamera-ichi-api.labanchen.workers.dev/api
    description: Direct Worker Endpoint
tags:
  - name: System
    description: 系統健康與中繼資料
  - name: Listings
    description: 二手相機、鏡頭刊登搜尋與詳情
  - name: Trends
    description: 二手攝影器材價格行情與均價走勢
  - name: Users
    description: 使用者與賣家資訊
  - name: MCP
    description: Model Context Protocol 代理人工具介面
  - name: Sandbox
    description: 免驗證公開沙盒環境，供 AI Agent 測試請求與回應格式
  - name: Batch
    description: 批次與非同步作業端點
  - name: Auth
    description: OAuth 2.0 與 Bearer token 憑證流程
paths:
  /auth/token:
    post:
      operationId: issueAccessToken
      summary: 取得 API 存取權杖
      description: 以 email 與密碼換取 Bearer access token，供代理人呼叫需驗證的端點。
      tags:
        - Auth
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  format: password
              required:
                - email
                - password
      responses:
        200:
          description: 成功取得權杖
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
                  token_type:
                    type: string
                    example: Bearer
                  expires_in:
                    type: integer
                    example: 3600
                  scope:
                    type: string
                    example: read:listings write:listings
                required:
                  - access_token
                  - token_type
                  - expires_in
        401:
          description: 帳號或密碼錯誤
          headers:
            WWW-Authenticate:
              description: RFC 6750 challenge with RFC 9728 resource metadata pointer
              schema:
                type: string
                example: Bearer realm="kamera-ichi", resource_metadata="https://kamera-ichi.com/.well-known/oauth-protected-resource"
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetails"
  /auth/revoke:
    post:
      operationId: revokeAccessToken
      summary: 撤銷存取權杖
      description: 立即撤銷目前的 Bearer access token 與所有 refresh token。
      tags:
        - Auth
      security:
        - BearerAuth:[]
      responses:
        200:
          description: 權杖已撤銷
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    example: true
                  revoked:
                    type: boolean
                    example: true
                required:
                  - ok
                  - revoked
        401:
          description: 缺少或無效的 Bearer token
          headers:
            WWW-Authenticate:
              description: RFC 6750 challenge with RFC 9728 resource metadata pointer
              schema:
                type: string
                example: Bearer realm="kamera-ichi", resource_metadata="https://kamera-ichi.com/.well-known/oauth-protected-resource"
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetails"
  /health:
    get:
      operationId: healthCheck
      summary: 系統健康檢查
      description: 檢查 API 服務與 D1 資料庫連線狀態。
      tags:
        - System
      responses:
        200:
          description: 服務正常運作
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  app:
                    type: string
                    example: Kamera-ichi API
                  database:
                    type: string
                    example: ok
                required:
                  - status
                  - app
                  - database
        503:
          description: 服務降級或資料庫異常
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /listings/search:
    get:
      operationId: searchListings
      summary: 搜尋二手攝影器材刊登
      description: 依關鍵字、分類、品牌、刊登類型、商品狀況、地區與價格區間搜尋台灣二手器材。
      tags:
        - Listings
      parameters:
        - name: q
          in: query
          description: 關鍵字搜尋（型號、標題或描述）
          required: false
          schema:
            type: string
            example: Sony A74
        - name: category
          in: query
          description: 器材分類
          required: false
          schema:
            type: string
            enum:
              - camera
              - lens
              - accessory
              - film
              - drone
              - lighting
            example: camera
        - name: brand
          in: query
          description: 品牌名稱
          required: false
          schema:
            type: string
            example: Sony
        - name: listing_type
          in: query
          description: "刊登類型（sell: 出售, seek: 徵求）"
          required: false
          schema:
            type: string
            enum:
              - sell
              - seek
            default: sell
        - name: condition
          in: query
          description: 商品狀況
          required: false
          schema:
            type: string
            enum:
              - new
              - like_new
              - excellent
              - good
              - used
              - broken
        - name: location
          in: query
          description: 台灣縣市（例：台北市、台中市、高雄市）
          required: false
          schema:
            type: string
            example: 台北市
        - name: price_min
          in: query
          description: 最低價格 (NTD)
          required: false
          schema:
            type: integer
            minimum: 0
            example: 20000
        - name: price_max
          in: query
          description: 最高價格 (NTD)
          required: false
          schema:
            type: integer
            minimum: 0
            example: 50000
        - name: sort
          in: query
          description: 排序方式
          required: false
          schema:
            type: string
            enum:
              - newest
              - price_asc
              - price_desc
            default: newest
        - name: page
          in: query
          description: 頁碼（從 1 開始）
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        - name: page_size
          in: query
          description: 每頁筆數（上限 100）
          required: false
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
      responses:
        200:
          description: 搜尋結果列表
          content:
            application/json:
              schema:
                type: object
                properties:
                  total:
                    type: integer
                    example: 42
                  page:
                    type: integer
                    example: 1
                  page_size:
                    type: integer
                    example: 20
                  items:
                    type: array
                    items:
                      $ref: "#/components/schemas/Listing"
                required:
                  - total
                  - page
                  - page_size
                  - items
        400:
          description: 無效的請求參數
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        429:
          description: 請求頻率過高
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /listings/{id}:
    get:
      operationId: getListingById
      summary: 取得單一商品刊登詳情
      description: 依刊登 UUID 取得完整器材規格、照片網址、賣家資訊與驗證狀態。
      tags:
        - Listings
      parameters:
        - name: id
          in: path
          required: true
          description: 商品刊登 UUID
          schema:
            type: string
            format: uuid
            example: 3f14c9eb-3341-4f3f-90b4-53a8c45a19b0
      responses:
        200:
          description: 商品完整資訊
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Listing"
        404:
          description: 商品不存在或已下架
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /listings/brands/{category}:
    get:
      operationId: getBrandsByCategory
      summary: 取得特定分類支援品牌清單
      description: 取得指定分類（相機、鏡頭、配件等）可用的品牌列表。
      tags:
        - Listings
      parameters:
        - name: category
          in: path
          required: true
          description: 分類名稱
          schema:
            type: string
            enum:
              - camera
              - lens
              - accessory
              - film
              - drone
              - lighting
      responses:
        200:
          description: 品牌名稱陣列
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
                example:
                  - Sony
                  - Canon
                  - Nikon
                  - Fujifilm
                  - Leica
  /trends/brands:
    get:
      operationId: getTrendsBrands
      summary: 取得各品牌刊登統計
      description: 列出各品牌在平台的商品刊登總數。
      tags:
        - Trends
      responses:
        200:
          description: 品牌與刊登數統計列表
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    brand:
                      type: string
                      example: Sony
                    count:
                      type: integer
                      example: 128
                  required:
                    - brand
                    - count
  /trends/trending:
    get:
      operationId: getTrendingModels
      summary: 取得近期熱門型號與行情指標
      description: 依最近刊登活躍度排序，輸出熱門相機與鏡頭型號行情。
      tags:
        - Trends
      parameters:
        - name: days
          in: query
          description: 統計天數區間（預設 365 天）
          required: false
          schema:
            type: integer
            default: 365
        - name: limit
          in: query
          description: 回傳筆數上限
          required: false
          schema:
            type: integer
            default: 20
        - name: category
          in: query
          description: 分類篩選
          required: false
          schema:
            type: string
            enum:
              - camera
              - lens
              - accessory
              - film
              - drone
              - lighting
      responses:
        200:
          description: 熱門型號列表
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/TrendingModel"
  /trends/indexable-models:
    get:
      operationId: getIndexableTrendModels
      summary: 取得可索引行情型號
      description: 以不分大小寫且去除外層空白的品牌與型號作為唯一 identity，列出 active 與 sold 刊登中、歷史樣本數至少 3 筆的型號；跨多分類時 category 為 null。
      tags:
        - Trends
      parameters:
        - name: limit
          in: query
          description: 回傳筆數上限（限制 1 至 1000）
          required: false
          schema:
            type: integer
            default: 1000
            minimum: 1
            maximum: 1000
      responses:
        200:
          description: 可索引行情型號列表
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/IndexableTrendModel"
        400:
          description: limit 參數格式錯誤
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /trends/{brand}/{model}:
    get:
      operationId: getModelTrends
      summary: 取得單一型號完整二手價格行情
      description: 取得指定品牌與型號的二手均價、歷史價格區間、成色分佈與走勢資料。
      tags:
        - Trends
      parameters:
        - name: brand
          in: path
          required: true
          description: 品牌名稱（例如 Sony, Fujifilm）
          schema:
            type: string
            example: Sony
        - name: model
          in: path
          required: true
          description: 型號名稱（例如 A74, X-T5）
          schema:
            type: string
            example: A74
      responses:
        200:
          description: 型號價格行情統計資料
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TrendsModelDetail"
        404:
          description: 無足夠刊登資料計算行情
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /sandbox:
    get:
      operationId: getSandboxIndex
      summary: 取得沙盒環境資訊
      description: 列出免驗證公開沙盒環境的可用端點，供 AI Agent 在不申請憑證的情況下測試請求與回應格式。
      tags:
        - Sandbox
      responses:
        200:
          description: 沙盒環境說明
          content:
            application/json:
              schema:
                type: object
                properties:
                  environment:
                    type: string
                    example: sandbox
                  description:
                    type: string
                  base_url:
                    type: string
                    format: uri
                  authentication:
                    type: string
                    example: none
                  endpoints:
                    type: array
                    items:
                      type: string
                required:
                  - environment
                  - base_url
                  - authentication
                  - endpoints
  /sandbox/v1:
    get:
      operationId: getSandboxV1Index
      summary: 取得沙盒 v1 環境資訊
      description: 列出免驗證沙盒 v1 的基底網址與可用端點。
      tags:
        - Sandbox
      responses:
        200:
          description: 沙盒 v1 說明
          content:
            application/json:
              schema:
                type: object
                properties:
                  environment:
                    type: string
                    example: sandbox
                  version:
                    type: string
                    example: v1
                  authentication:
                    type: string
                    example: none
                  base_url:
                    type: string
                    format: uri
                  endpoints:
                    type: array
                    items:
                      type: string
                  documentation:
                    type: string
                    format: uri
                required:
                  - environment
                  - version
                  - authentication
                  - base_url
                  - endpoints
  /sandbox/v1/listings/search:
    get:
      operationId: sandboxSearchListings
      summary: 沙盒：搜尋二手器材刊登
      description: 以固定測試資料回傳搜尋結果，回應結構與正式端點一致，且支援 cursor 分頁欄位。
      tags:
        - Sandbox
      parameters:
        - name: q
          in: query
          required: false
          description: 關鍵字搜尋
          schema:
            type: string
            example: Sony
        - name: cursor
          in: query
          required: false
          description: 游標分頁指標，帶入前一次回應的 next_cursor
          schema:
            type: string
            nullable: true
      responses:
        200:
          description: 沙盒搜尋結果
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CursorPaginatedListings"
  /sandbox/v1/trends/{brand}/{model}:
    get:
      operationId: sandboxGetModelTrends
      summary: 沙盒：取得型號行情
      description: 以固定測試資料回傳行情統計，結構與正式行情端點一致。
      tags:
        - Sandbox
      parameters:
        - name: brand
          in: path
          required: true
          description: 品牌名稱
          schema:
            type: string
            example: Sony
        - name: model
          in: path
          required: true
          description: 型號名稱
          schema:
            type: string
            example: A74
      responses:
        200:
          description: 沙盒行情統計
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TrendsModelDetail"
  /sandbox/v1/batch:
    post:
      operationId: sandboxBatchOperations
      summary: 批次操作端點
      description: 在單一請求中送出最多 20 個子操作，並以 Idempotency-Key 確保重送安全。
      tags:
        - Batch
        - Sandbox
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: 冪等鍵。相同鍵值重送時回傳相同結果，避免重複寫入。
          schema:
            type: string
            example: 9f1d7c1e-1f0a-4d2b-8a1e-2b7c9d0e5f31
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchRequest"
      responses:
        200:
          description: 批次執行結果
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchResponse"
        400:
          description: 請求格式錯誤
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetails"
  /sandbox/v1/jobs:
    post:
      operationId: createAsyncJob
      summary: 建立非同步作業
      description: 建立長時間執行的非同步作業，立即回傳 202 與輪詢網址（async job pattern）。
      tags:
        - Batch
        - Sandbox
      responses:
        202:
          description: 作業已接受，請輪詢 poll_url
          headers:
            Location:
              description: 作業狀態輪詢網址
              schema:
                type: string
                format: uri
            Retry-After:
              description: 建議的輪詢間隔秒數
              schema:
                type: integer
                example: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncJob"
  /sandbox/v1/jobs/{job_id}:
    get:
      operationId: getAsyncJobStatus
      summary: 查詢非同步作業狀態
      description: 輪詢非同步作業的執行狀態與結果。
      tags:
        - Batch
        - Sandbox
      parameters:
        - name: job_id
          in: path
          required: true
          description: 作業識別碼
          schema:
            type: string
      responses:
        200:
          description: 作業狀態
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncJob"
  /mcp:
    post:
      operationId: mcpJsonRpc
      summary: Model Context Protocol (MCP) Streamable HTTP 端點
      description: 提供 AI Agent 透過 JSON-RPC 2.0 呼叫器材市集工具（搜尋、行情查詢、商品詳情）。
      tags:
        - MCP
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/McpRequest"
      responses:
        200:
          description: JSON-RPC 2.0 回應
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/McpResponse"
        400:
          description: 無效的 JSON-RPC 格式
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    get:
      operationId: getMcpInfo
      summary: MCP 服務資訊與 Manifest 探索
      description: 回傳 MCP 伺服器規格、支援工具清單與連線說明。
      tags:
        - MCP
      responses:
        200:
          description: MCP 伺服器資訊
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    example: kamera-ichi
                  version:
                    type: string
                    example: "1.0.0"
                  description:
                    type: string
                  manifest_url:
                    type: string
                    example: https://kamera-ichi.com/.well-known/mcp/manifest.json
                  transport:
                    type: string
                    example: http
                  endpoint:
                    type: string
                    example: https://kamera-ichi.com/api/mcp
                  tools:
                    type: array
                    items:
                      type: string
                    example:
                      - search_listings
                      - get_listing
                      - get_trends
                      - get_trending_models
                      - get_brands
components:
  securitySchemes:
    OAuth2:
      type: oauth2
      description: OAuth 2.0 authorization for Kamera-ichi API operations
      flows:
        authorizationCode:
          authorizationUrl: https://kamera-ichi.com/login
          tokenUrl: https://kamera-ichi.com/api/auth/token
          scopes:
            read:listings: Read public and active camera/lens listings and price trends
            write:listings: Create, edit, and manage own camera/lens listings
            read:profile: Read user account profile and settings
            write:messages: Send and receive buyer/seller direct messages
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT session authentication for member operations
  schemas:
    CursorPaginatedListings:
      type: object
      properties:
        total:
          type: integer
          example: 2
        page:
          type: integer
          example: 1
        page_size:
          type: integer
          example: 20
        cursor:
          type: string
          nullable: true
          description: 目前游標
        next_cursor:
          type: string
          nullable: true
          description: 下一頁游標，為 null 時代表已無更多資料
        items:
          type: array
          items:
            $ref: "#/components/schemas/Listing"
      required:
        - total
        - items
        - next_cursor
    BatchRequest:
      type: object
      properties:
        operations:
          type: array
          maxItems: 20
          description: 子操作陣列，單次最多 20 筆
          items:
            type: object
            properties:
              id:
                type: string
                description: 呼叫端自訂的子操作識別碼
              method:
                type: string
                enum:
                  - GET
                  - POST
                  - PATCH
                  - DELETE
              path:
                type: string
                example: /v1/listings/search?q=Sony
              body:
                type: object
                nullable: true
            required:
              - method
              - path
      required:
        - operations
    BatchResponse:
      type: object
      properties:
        idempotency_key:
          type: string
          nullable: true
        count:
          type: integer
          example: 2
        responses:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              status:
                type: integer
                example: 200
              body:
                type: object
            required:
              - id
              - status
      required:
        - count
        - responses
    AsyncJob:
      type: object
      properties:
        job_id:
          type: string
          example: job_mt70nlzv
        status:
          type: string
          enum:
            - accepted
            - running
            - succeeded
            - failed
        poll_url:
          type: string
          format: uri
        result:
          type: object
          nullable: true
      required:
        - job_id
        - status
    ProblemDetails:
      type: object
      properties:
        type:
          type: string
          format: uri
          example: https://kamera-ichi.com/errors/not_found
        title:
          type: string
          example: Not Found
        status:
          type: integer
          example: 404
        detail:
          type: string
          example: The requested listing or route could not be found.
        instance:
          type: string
          format: uri
          example: /api/listings/123
        code:
          type: string
          example: NOT_FOUND
        resolution:
          type: string
          example: Check the listing ID or browse active listings at /api/listings/search.
      required:
        - type
        - title
        - status
        - detail
        - code
        - resolution
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: NOT_FOUND
            message:
              type: string
              example: Resource not found
            hint:
              type: string
              example: Check the URL path or refer to /openapi.json for available endpoints.
          required:
            - code
            - message
        detail:
          type: string
          example: Resource not found
      required:
        - error
        - detail
    Listing:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 3f14c9eb-3341-4f3f-90b4-53a8c45a19b0
        title:
          type: string
          example: Sony A7 IV (A74) 機身 95成新 盒單全
        category:
          type: string
          enum:
            - camera
            - lens
            - accessory
            - film
            - drone
            - lighting
          example: camera
        listing_type:
          type: string
          enum:
            - sell
            - seek
          example: sell
        brand:
          type: string
          example: Sony
        model:
          type: string
          example: A74
        condition:
          type: string
          enum:
            - new
            - like_new
            - excellent
            - good
            - used
            - broken
          example: excellent
        price:
          type: integer
          nullable: true
          example: 48000
        price_min:
          type: integer
          nullable: true
        price_max:
          type: integer
          nullable: true
        negotiable:
          type: boolean
          example: true
        description:
          type: string
          example: 快門數約 8000，防潮箱常駐，原廠盒單齊全附二顆原廠電池。
        location:
          type: string
          example: 台北市
        can_ship:
          type: boolean
          example: false
        shutter_count:
          type: integer
          nullable: true
          example: 8000
        has_box:
          type: boolean
          example: true
        has_receipt:
          type: boolean
          example: true
        status:
          type: string
          enum:
            - active
            - sold
            - reserved
            - removed
          example: active
        created_at:
          type: string
          format: date-time
          example: 2026-08-15T08:30:00Z
        images:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                format: uuid
              url:
                type: string
                format: uri
              is_cover:
                type: boolean
        seller:
          type: object
          properties:
            id:
              type: string
              format: uuid
            nickname:
              type: string
              example: Laban
            avatar_url:
              type: string
              format: uri
              nullable: true
            rating:
              type: number
              example: 5
            review_count:
              type: integer
              example: 12
            is_featured:
              type: boolean
              example: true
      required:
        - id
        - title
        - category
        - listing_type
        - brand
        - model
        - status
        - created_at
    IndexableTrendModel:
      type: object
      properties:
        brand:
          type: string
          example: Sony
        model:
          type: string
          example: A74
        category:
          type: string
          nullable: true
          example: camera
        listing_count:
          type: integer
          minimum: 3
          example: 18
      required:
        - brand
        - model
        - category
        - listing_count
    TrendingModel:
      type: object
      properties:
        brand:
          type: string
          example: Sony
        model:
          type: string
          example: A74
        category:
          type: string
          example: camera
        avg_price:
          type: number
          example: 47500
        min_price:
          type: number
          example: 42000
        max_price:
          type: number
          example: 52000
        recent_count:
          type: integer
          example: 15
      required:
        - brand
        - model
        - avg_price
        - min_price
        - max_price
    TrendsModelDetail:
      type: object
      properties:
        brand:
          type: string
          example: Sony
        model:
          type: string
          example: A74
        category:
          type: string
          nullable: true
          example: camera
        avg_price:
          type: number
          example: 47500
        min_price:
          type: number
          example: 42000
        max_price:
          type: number
          example: 52000
        listing_count:
          type: integer
          example: 18
        by_condition:
          type: array
          items:
            type: object
            properties:
              condition:
                type: string
                example: excellent
              condition_label:
                type: string
                example: 極佳品
              count:
                type: integer
                example: 8
              avg_price:
                type: number
                example: 48000
        price_history:
          type: array
          items:
            type: object
            properties:
              month:
                type: string
                example: 2026-07
              avg_price:
                type: number
                example: 48000
              count:
                type: integer
                example: 5
      required:
        - brand
        - model
        - avg_price
        - min_price
        - max_price
        - listing_count
    McpRequest:
      type: object
      properties:
        jsonrpc:
          type: string
          example: "2.0"
        id:
          type:
            - string
            - number
          example: 1
        method:
          type: string
          example: tools/call
        params:
          type: object
      required:
        - jsonrpc
        - method
    McpResponse:
      type: object
      properties:
        jsonrpc:
          type: string
          example: "2.0"
        id:
          type:
            - string
            - number
          example: 1
        result:
          type: object
        error:
          type: object
          properties:
            code:
              type: integer
              example: -32601
            message:
              type: string
              example: Method not found
      required:
        - jsonrpc