openapi: 3.1.0
info:
  title: NailCloud Open Developer Platform API
  version: 1.0.0
  description: |
    独立的开放开发者平台。所有 /api/open/v1 请求使用 API Key、时间戳、Nonce 与 HMAC-SHA256；API Secret 不会作为请求头发送。
servers:
  - url: https://developer.nailcloud.cn
    description: Production placeholder; configure the actual Developer Platform host before publishing.
paths:
  /api/open/v1/products:
    get:
      summary: List product projections
      security: [{ NailCloudHmac: [] }]
      responses:
        '200': { $ref: '#/components/responses/ProjectionList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /api/open/v1/skus:
    get:
      summary: List SKU projections
      security: [{ NailCloudHmac: [] }]
      responses:
        '200': { $ref: '#/components/responses/ProjectionList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /api/open/v1/orders:
    get:
      summary: List scoped orders
      description: Requires orders:read. The caller must be partner-bound or provide an authorized x-nailcloud-customer-ref.
      security: [{ NailCloudHmac: [] }]
      parameters: [{ $ref: '#/components/parameters/CustomerRef' }]
      responses:
        '200': { $ref: '#/components/responses/ProjectionList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      summary: Create an order through the Partner Platform integration contract
      description: Requires orders:create and a partner-bound developer app. partner_no and source attribution are enforced server-side. Supply a stable idempotency_key.
      security: [{ NailCloudHmac: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [idempotency_key]
              properties:
                idempotency_key: { type: string, maxLength: 128 }
                customer_no: { type: string }
                product_code: { type: string }
                sku_code: { type: string }
                quantity: { type: integer, minimum: 1 }
              additionalProperties: true
      responses:
        '201': { $ref: '#/components/responses/OrderCreated' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '422': { $ref: '#/components/responses/PartnerOrderError' }
  /api/open/v1/activation-tasks:
    get:
      summary: List scoped activation task projections
      security: [{ NailCloudHmac: [] }]
      parameters: [{ $ref: '#/components/parameters/CustomerRef' }]
      responses:
        '200': { $ref: '#/components/responses/ProjectionList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /api/open/v1/external-accounts:
    get:
      summary: List scoped external account projections
      security: [{ NailCloudHmac: [] }]
      parameters: [{ $ref: '#/components/parameters/CustomerRef' }]
      responses:
        '200': { $ref: '#/components/responses/ProjectionList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /api/open/v1/balances:
    get:
      summary: List scoped balance and quota fields
      security: [{ NailCloudHmac: [] }]
      parameters: [{ $ref: '#/components/parameters/CustomerRef' }]
      responses:
        '200': { $ref: '#/components/responses/ProjectionList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /api/open/v1/mcp:
    post:
      summary: MCP JSON-RPC runtime
      description: Supports initialize, tools/list, tools/call, resources/list, resources/read, prompts/list and prompts/get. Published definitions, required scopes and prompt argument validation are enforced.
      security: [{ NailCloudHmac: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/JsonRpcRequest' }
      responses:
        '200':
          description: JSON-RPC result or error envelope
          content:
            application/json:
              schema: { type: object, additionalProperties: true }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
  /api/open/v1/skills:
    get:
      summary: List published Skills available to the current app
      security: [{ NailCloudHmac: [] }]
      responses:
        '200': { $ref: '#/components/responses/ProjectionList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
  /api/open/v1/skills/{skillId}/run:
    post:
      summary: Run a published Skill
      security: [{ NailCloudHmac: [] }]
      parameters:
        - name: skillId
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object, additionalProperties: true }
      responses:
        '200':
          description: Skill result
          content:
            application/json:
              schema: { type: object, additionalProperties: true }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/SkillHandlerUnavailable' }
components:
  securitySchemes:
    NailCloudHmac:
      type: apiKey
      in: header
      name: x-nailcloud-api-key
      description: |
        Send x-nailcloud-api-key, x-nailcloud-timestamp, x-nailcloud-nonce and x-nailcloud-signature. Signature canonical form is timestamp + "\n" + nonce + "\n" + HTTP method + "\n" + path-and-query + "\n" + SHA256(raw body). Signature value is sha256=HMAC-SHA256(secret, canonical). Nonces are single-use.
  parameters:
    CustomerRef:
      name: x-nailcloud-customer-ref
      in: header
      required: false
      description: Customer number; requires an active customer authorization that contains the route scope.
      schema: { type: string }
  schemas:
    JsonRpcRequest:
      type: object
      required: [jsonrpc, method]
      properties:
        jsonrpc: { type: string, const: '2.0' }
        id: { oneOf: [{ type: string }, { type: integer }, { type: 'null' }] }
        method: { type: string }
        params: { type: object, additionalProperties: true }
    ProjectionList:
      type: object
      required: [items, trace_id]
      properties:
        items:
          type: array
          items: { type: object, additionalProperties: true }
        trace_id: { type: string }
    Error:
      type: object
      required: [code, message]
      properties:
        code: { type: string }
        message: { type: string }
        trace_id: { type: string }
  responses:
    ProjectionList:
      description: Projection list
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ProjectionList' }
    OrderCreated:
      description: Created or idempotently returned partner order
      content:
        application/json:
          schema:
            type: object
            required: [order, trace_id]
            properties:
              order: { type: object, additionalProperties: true }
              trace_id: { type: string }
    Unauthorized:
      description: Invalid key, timestamp, nonce or HMAC signature
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
    Forbidden:
      description: Missing scope or owner/customer authorization
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
    BadRequest:
      description: Invalid JSON or protocol payload
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
    NotFound:
      description: Requested route or published definition was not found
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
    PartnerOrderError:
      description: Partner Platform rejected the order business rules
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
    SkillHandlerUnavailable:
      description: Published Skill has no enabled runtime handler
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
