openapi: 3.1.0
info:
  title: Shugoi API
  version: 1.0.0
  description: API de décision anti-abus et opérations de compte actuellement disponibles.
  license:
    name: Documentation propriétaire; SDK distribués sous licence MIT
    url: https://shugoi.com/tos
servers:
  - url: https://shugoi.com
tags:
  - name: Protection
    description: Décisions et événements de la couche anti-abus.
  - name: Account
    description: Opérations authentifiées et récupération de compte.
paths:
  /api/v1/check:
    post:
      tags: [Protection]
      operationId: checkDecision
      security: []
      summary: Évaluer une action protégée
      description: Les intégrations serveur signent le corps avec le secret de signature. Les appels navigateur sont limités à une origine autorisée.
      parameters:
        - $ref: '#/components/parameters/ShugoiSignature'
        - $ref: '#/components/parameters/ShugoiTimestamp'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckRequest'
      responses:
        '200':
          description: Décision rendue. Une réponse refusée reste une réponse HTTP réussie.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /api/v1/event:
    post:
      tags: [Protection]
      operationId: recordGuardEvent
      security: []
      summary: Enregistrer un blocage produit par le guard
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [siteKey, reason]
              properties:
                siteKey: { $ref: '#/components/schemas/SiteKey' }
                reason:
                  type: string
                  enum: [tor_browser, virtual_machine, headless_browser, anti_detect_browser, content_replacement, whitelist_blocked, connection_failed]
                machineId:
                  type: string
                  pattern: '^[a-f0-9]{64}$'
      responses:
        '200':
          description: Événement accepté
          content:
            application/json:
              schema:
                type: object
                required: [ok]
                properties: { ok: { type: boolean, const: true } }
        '400': { $ref: '#/components/responses/BadRequest' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /api/auth/forgot:
    post:
      tags: [Account]
      operationId: requestPasswordReset
      security: []
      summary: Demander un lien de réinitialisation
      description: La réponse ne révèle pas si l'adresse possède un compte.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [email]
              properties:
                email: { type: string, format: email }
                lang: { type: string, enum: [fr, en] }
      responses:
        '200':
          description: Demande acceptée, que le compte existe ou non
          content:
            application/json:
              schema:
                type: object
                required: [success]
                properties: { success: { type: boolean, const: true } }
        '400': { $ref: '#/components/responses/BadRequest' }
  /api/dashboard/sites:
    get:
      tags: [Account]
      operationId: listSites
      summary: Lister les sites accessibles
      security: [{ sessionCookie: [] }]
      responses:
        '200': { description: Sites du compte }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      tags: [Account]
      operationId: manageSite
      summary: Créer, vérifier ou administrer une clé de site
      security: [{ sessionCookie: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                action: { type: string, enum: [verify, check, rotate_secret, reverify] }
                mode: { type: string, enum: [live, test] }
                hostname: { type: string }
                allowedOrigins:
                  type: array
                  items: { type: string, format: uri }
      responses:
        '200': { description: Opération terminée }
        '201': { description: Site créé; le secret n'est renvoyé qu'une fois }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /api/dashboard/account-export:
    get:
      tags: [Account]
      operationId: exportAccountData
      summary: Télécharger les données portables du compte
      description: Les mots de passe, secrets, empreintes réseau et données techniques des visiteurs sont exclus.
      security: [{ sessionCookie: [] }]
      responses:
        '200':
          description: Export JSON sans cache
          headers:
            Content-Disposition:
              schema: { type: string }
          content:
            application/json:
              schema:
                type: object
                required: [schemaVersion, exportedAt, account, team, sites, usage, sessions, legalAcceptances]
                properties:
                  schemaVersion: { type: integer, const: 1 }
                  exportedAt: { type: string, format: date-time }
                  account: { type: [object, 'null'] }
                  team: { type: array, items: { type: object } }
                  sites: { type: array, items: { type: object } }
                  usage: { type: array, items: { type: object } }
                  subscription: { type: [object, 'null'] }
                  sessions: { type: array, items: { type: object } }
                  legalAcceptances: { type: array, items: { type: object } }
        '401': { $ref: '#/components/responses/Unauthorized' }
components:
  securitySchemes:
    sessionCookie:
      type: apiKey
      in: cookie
      name: shugoi_session
  parameters:
    ShugoiSignature:
      name: X-Shugoi-Signature
      in: header
      required: false
      description: HMAC SHA-256 hexadécimal du timestamp et du corps, produit par les SDK serveur.
      schema: { type: string }
    ShugoiTimestamp:
      name: X-Shugoi-Timestamp
      in: header
      required: false
      schema: { type: string }
  schemas:
    SiteKey:
      type: string
      pattern: '^sg_sk_(live|test)_[A-Za-z0-9]+$'
    CheckRequest:
      type: object
      required: [siteKey, action]
      properties:
        siteKey: { $ref: '#/components/schemas/SiteKey' }
        action: { type: string, minLength: 1 }
        fingerprint:
          type: object
          properties:
            browser: { type: string }
            machineId: { type: string }
        metadata:
          type: object
          properties: { email: { type: string, format: email } }
        signals: { type: object }
        captchaToken: { type: string }
        passToken: { type: string }
    CheckResponse:
      type: object
      required: [allowed, remaining, limit, resetAt, requestId]
      properties:
        allowed: { type: boolean }
        blocked: { type: boolean }
        blocked_reason: { type: string }
        reason: { type: string }
        remaining: { type: integer, minimum: 0 }
        limit: { type: integer, minimum: 0 }
        resetAt: { type: integer }
        risk: { type: string, enum: [low, medium, high] }
        requestId: { type: string, pattern: '^sg_rq_' }
        captcha: { type: [object, 'null'] }
    Error:
      type: object
      required: [error]
      properties:
        error: { type: string }
        requestId: { type: string }
  responses:
    BadRequest:
      description: Requête invalide
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
    Unauthorized:
      description: Authentification, origine, clé ou signature invalide
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
    Forbidden:
      description: Opération non autorisée pour ce compte
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
    RateLimited:
      description: Limite temporaire atteinte
      content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }
