openapi: 3.1.0
info:
  title: Growly Personality API
  version: "1.0.0"
  summary: DISC + OCEAN trait estimation desde texto público B2B (LLM-based, no validated psychometric instrument).
  description: |
    Devuelve perfil DISC primario/secundario + scores 0-100 + confidence calibrada
    + estimación OCEAN (Big-5 traits) — todo en una sola call.

    **Naturaleza del output:** trait estimation desde texto público vía LLM
    (Gemini / Claude). NO es un test psicométrico validado (NEO-PI-R, IPIP-NEO).
    Las inferencias son hipótesis para asistencia comercial — el SDR humano
    retiene control editorial. NO usar para decisiones de hiring, promoción
    o evaluación de personal (TOS prohíbe explícitamente uso HR).

    **Pricing:** $0.05 / análisis (PAYG). Plan mensual $39 / 1000 análisis.

    **Auth:** header `X-API-Key`. Generar en https://app.ingrowthsolutions.com/admin/api-key.

    **Rate limit:** 60 calls/min/key.
  contact:
    email: api@ingrowthsolutions.com
    url: https://growly.io/contact
  license:
    name: Growly Commercial License
    url: https://app.ingrowthsolutions.com/legal/terms

servers:
  - url: https://app.ingrowthsolutions.com
    description: Production

paths:
  /api/v1/personality/analyze:
    post:
      summary: Analizar personalidad (DISC + Big-5)
      description: |
        Toma corpus textual del prospecto (LinkedIn About, posts, news mentions)
        y devuelve DISC + Big-5 inferidos por LLM. Confidence calibrada según
        riqueza del corpus.
      operationId: analyzePersonality
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [text]
              properties:
                text:
                  type: string
                  minLength: 80
                  description: Corpus textual del prospecto (LinkedIn bio + posts + meeting notes, etc).
                  example: "VP de Operaciones en cadena retail. 15 años en logística e inventarios. Cerramos Q1 con +28% en sell-through..."
                leadName:
                  type: string
                  description: Nombre del prospecto (opcional, mejora el prompt).
                  example: "Laura Garza"
                cargo:
                  type: string
                  description: Cargo declarado (opcional).
                  example: "VP de Operaciones"
                company:
                  type: string
                  description: Empresa (opcional).
                  example: "Cinépolis"
      responses:
        '200':
          description: Análisis exitoso. Costo $0.05 facturado al API key.
          content:
            application/json:
              schema:
                type: object
                properties:
                  disc:
                    type: object
                    properties:
                      primary:    { type: string, enum: [D, I, S, C] }
                      secondary:  { type: string, enum: [D, I, S, C] }
                      scores:
                        type: object
                        properties:
                          D: { type: number, minimum: 0, maximum: 100 }
                          I: { type: number, minimum: 0, maximum: 100 }
                          S: { type: number, minimum: 0, maximum: 100 }
                          C: { type: number, minimum: 0, maximum: 100 }
                      confidence: { type: number, minimum: 0, maximum: 1 }
                  big5:
                    type: object
                    properties:
                      openness:          { type: number, minimum: 0, maximum: 100 }
                      conscientiousness: { type: number, minimum: 0, maximum: 100 }
                      extraversion:      { type: number, minimum: 0, maximum: 100 }
                      agreeableness:     { type: number, minimum: 0, maximum: 100 }
                      neuroticism:       { type: number, minimum: 0, maximum: 100 }
                  justification:
                    type: string
                    description: 1 frase ≤30 palabras citando señales del corpus.
                  model:
                    type: string
                    description: Modelo LLM real usado (router A/B).
                    example: "gemini-2.5-flash"
                  analyzed_at: { type: string, format: date-time }
                  latency_ms:  { type: integer }
                  cost_usd:    { type: number }
        '400':
          description: Bad request — text muy corto o JSON inválido
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '401':
          description: API key faltante o inválida
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '429':
          description: Rate limit excedido (60/min/key)
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - type: object
                    properties:
                      retry_after_seconds: { type: integer }
        '502':
          description: Error en el LLM upstream
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key generada en /admin/api-key.

  schemas:
    Error:
      type: object
      required: [error, message]
      properties:
        error:   { type: string, description: 'Código corto, machine-readable' }
        message: { type: string, description: 'Descripción humana' }
