openapi: 3.1.0
info:
  title: Livoxa Public API
  version: 1.0.0
  description: API multi-tenant versionnée pour commandes, catalogue, stocks et rapports opérationnels. Les clés peuvent imposer une signature Ed25519 et, derrière un proxy de confiance, une validation mTLS.
servers:
  - url: /api/v1
    description: URL relative au domaine sur lequel Livoxa est déployé.
security:
  - bearerApiKey: []
  - bearerApiKey: []
    signedRequest: []
paths:
  /orders:
    get:
      summary: Lister les commandes
      operationId: listOrders
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - { name: status, in: query, schema: { type: string } }
        - { name: updated_after, in: query, schema: { type: string, format: date-time } }
      responses:
        '200': { $ref: '#/components/responses/Paginated' }
        '401': { $ref: '#/components/responses/Error' }
        '429': { $ref: '#/components/responses/Error' }
    post:
      summary: Créer une commande
      operationId: createOrder
      parameters: [ { $ref: '#/components/parameters/IdempotencyKey' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [branch_id, order]
              properties:
                branch_id: { type: string, format: uuid }
                order: { $ref: '#/components/schemas/OrderInput' }
      responses:
        '201': { $ref: '#/components/responses/Data' }
        '401': { $ref: '#/components/responses/Error' }
        '422': { $ref: '#/components/responses/Error' }
  /orders/{orderId}:
    parameters:
      - { name: orderId, in: path, required: true, schema: { type: string, format: uuid } }
    get:
      summary: Lire une commande, ses preuves et son suivi
      operationId: getOrder
      responses:
        '200': { $ref: '#/components/responses/Data' }
        '404': { $ref: '#/components/responses/Error' }
    patch:
      summary: Modifier une commande avant traitement
      operationId: updateOrder
      parameters: [ { $ref: '#/components/parameters/IdempotencyKey' } ]
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [order], properties: { order: { $ref: '#/components/schemas/OrderInput' } } } } }
      responses:
        '200': { $ref: '#/components/responses/Data' }
        '409': { $ref: '#/components/responses/Error' }
        '422': { $ref: '#/components/responses/Error' }
    delete:
      summary: Annuler une commande
      operationId: cancelOrder
      parameters: [ { $ref: '#/components/parameters/IdempotencyKey' } ]
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [reason], properties: { reason: { type: string, minLength: 3, maxLength: 500 } } } } }
      responses:
        '200': { $ref: '#/components/responses/Data' }
        '404': { $ref: '#/components/responses/Error' }
  /products:
    get:
      summary: Lister les produits et variantes
      operationId: listProducts
      parameters: [ { $ref: '#/components/parameters/Cursor' }, { $ref: '#/components/parameters/Limit' } ]
      responses:
        '200': { $ref: '#/components/responses/Paginated' }
  /stocks:
    get:
      summary: Lister les niveaux de stock
      operationId: listStock
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - { name: warehouse_id, in: query, schema: { type: string, format: uuid } }
      responses:
        '200': { $ref: '#/components/responses/Paginated' }
  /reports/operations:
    get:
      summary: Lire un rapport opérationnel ciblé
      operationId: getOperationsReport
      parameters:
        - { name: branch_id, in: query, schema: { type: string, format: uuid } }
        - { name: from, in: query, schema: { type: string, format: date-time } }
        - { name: to, in: query, schema: { type: string, format: date-time } }
      responses:
        '200': { $ref: '#/components/responses/Data' }
components:
  securitySchemes:
    bearerApiKey: { type: http, scheme: bearer, bearerFormat: LivoxaApiKey }
    signedRequest:
      type: apiKey
      in: header
      name: X-Livoxa-Signature
      description: "Signature Ed25519 en base64 au format ed25519=<signature>. La chaîne canonique contient méthode, chemin avec query, timestamp ISO 8601, nonce et SHA-256 hexadécimal du corps, séparés par un retour à la ligne. X-Livoxa-Key-Id, X-Livoxa-Timestamp et X-Livoxa-Nonce sont également requis."
  parameters:
    Cursor: { name: cursor, in: query, schema: { type: string }, description: Curseur opaque renvoyé par la page précédente. }
    Limit: { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 50 } }
    IdempotencyKey: { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 200 } }
  schemas:
    OrderInput:
      type: object
      additionalProperties: true
      description: Structure fonctionnelle complète documentée dans le cahier des charges Livoxa.
    Meta:
      type: object
      required: [request_id, api_version]
      properties:
        request_id: { type: string, format: uuid }
        api_version: { type: string, const: v1 }
    DataEnvelope:
      type: object
      required: [data, meta]
      properties: { data: {}, meta: { $ref: '#/components/schemas/Meta' } }
    ErrorEnvelope:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, request_id]
          properties:
            code: { type: string }
            message: { type: string }
            details: { type: object }
            request_id: { type: string, format: uuid }
  responses:
    Data: { description: Succès, content: { application/json: { schema: { $ref: '#/components/schemas/DataEnvelope' } } } }
    Paginated: { description: "Page avec items, next_cursor et has_more dans data", content: { application/json: { schema: { $ref: '#/components/schemas/DataEnvelope' } } } }
    Error: { description: Erreur structurée, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } } }
