openapi: 3.0.3
info:
  title: Claix API
  description: >
    Claix es una API de conversión de datos tabulares, JSON y documentos
    (PDF, Word, texto plano, imágenes) pensada para integraciones server-to-server
    (backends, scripts, herramientas de automatización como n8n, Zapier o
    Make). Expone cinco endpoints principales:

    - **Excel/CSV → JSON**: recibe un archivo Excel o CSV y lo transforma en
      JSON según la estructura definida en un "schema" previamente creado.
    - **JSON → Excel**: recibe uno o varios documentos JSON y devuelve un
      archivo Excel (.xlsx) binario, según la estructura definida en un
      "schema" previamente creado.
    - **PDF → JSON**: recibe un archivo PDF (de texto o escaneado) y devuelve
      un objeto JSON con los datos extraídos del documento, según la
      estructura definida en un "schema" previamente creado.
    - **Documento → JSON**: recibe un archivo de documento (.docx, .txt, .md
      o .rtf) y devuelve un objeto JSON con los datos extraídos, según la
      estructura definida en un "schema" previamente creado.
    - **Imagen → JSON**: recibe una imagen (.jpeg, .jpg, .png, .webp, .heic
      o .heif) y devuelve un objeto JSON con los datos extraídos del
      contenido visible, según la estructura definida en un "schema"
      previamente creado.

    Además, expone un endpoint de solo lectura para consultar los schemas
    ya creados en la cuenta:

    - **Listar schemas**: dado un API key, devuelve todos los schemas de esa
      cuenta con su información completa (id, nombre, tipo y definición).

    Los cinco endpoints de conversión usan reconocimiento automático de sinónimos,
    abreviaturas, traducciones y variantes de nombre entre las
    columnas/claves/campos de entrada y las propiedades definidas en el
    schema correspondiente.

    **Modo Agente** — Cuatro endpoints adicionales bajo `/agent/*-json` ejecutan
    la misma extracción estructurada que sus equivalentes `/api/*-json` y, a
    continuación, una fase de razonamiento semántico según `agent_definition`
    del schema. La respuesta HTTP 200 incluye los campos habituales de
    extracción más `agent_data` (objeto tipado con booleanos, enteros, strings
    u opciones cerradas). El schema debe tener `is_agent_mode` activado.
  version: "1.5.0"
servers:
  - url: https://claix.dev/api
    description: Endpoints de extracción estándar

security:
  - ApiKeyAuth: []
  - BearerAuth: []

tags:
  - name: Excel a JSON
    description: Conversión de archivos Excel/CSV a JSON estructurado
  - name: JSON a Excel
    description: Conversión de datos JSON a archivos Excel binarios
  - name: PDF a JSON
    description: Extracción de datos estructurados a partir de archivos PDF
  - name: Documento a JSON
    description: >
      Extracción de datos estructurados a partir de documentos de texto
      (.docx, .txt, .md, .rtf)
  - name: Imagen a JSON
    description: >
      Extracción de datos estructurados a partir de imágenes
      (.jpeg, .jpg, .png, .webp, .heic, .heif)
  - name: Schemas
    description: Consulta de los schemas creados en la cuenta
  - name: Modo Agente
    description: >
      Extracción estructurada más razonamiento semántico en una sola llamada.
      Requiere schema con is_agent_mode activado y agent_definition configurada.
      URLs públicas: POST /agent/excel-json, /agent/pdf-json, /agent/doc-json,
      /agent/img-json (documentación humana en /documentation/agent/*).

paths:
  /excel-json:
    post:
      operationId: excelToJson
      tags:
        - Excel a JSON
      summary: Convierte un archivo Excel o CSV a JSON según un schema
      description: >
        Recibe un archivo .xlsx o .csv y un schema_id, y devuelve los datos
        transformados en JSON con las claves exactamente iguales a las
        propiedades definidas en el schema. Si el archivo tiene varias hojas,
        solo se procesa la primera.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ExcelJsonRequest'
      responses:
        '200':
          description: Archivo procesado y transformado correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExcelJsonSuccessResponse'
        '400':
          description: Petición inválida (archivo o schema_id incorrectos/faltantes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: El schema_id no existe o no pertenece a la cuenta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: No se encontró correspondencia entre columnas y schema
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Fallo del servicio de IA encargado de interpretar el archivo
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /json-excel:
    post:
      operationId: jsonToExcel
      tags:
        - JSON a Excel
      summary: Convierte uno o varios documentos JSON a un archivo Excel
      description: >
        Recibe datos JSON (como archivo, texto plano o cuerpo JSON puro) y un
        schema_id, y devuelve un archivo .xlsx binario con las columnas en el
        orden y nombres definidos por el schema. Admite multipart/form-data
        (uno o varios campos con JSON) o application/json puro (array,
        objeto único, o sobre con schema_id + data/records).
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/JsonExcelMultipartRequest'
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/JsonExcelArrayRequest'
                - $ref: '#/components/schemas/JsonExcelSingleObjectRequest'
                - $ref: '#/components/schemas/JsonExcelEnvelopeRequest'
      parameters:
        - name: schema_id
          in: query
          required: false
          description: >
            UUID del schema a utilizar. Alternativa a incluirlo en el cuerpo
            JSON (solo aplica cuando se usa application/json puro sin sobre).
          schema:
            type: string
            format: uuid
            example: 1f9e6103-9221-4c22-8a3a-8592d8b0eb38
      responses:
        '200':
          description: Archivo Excel generado correctamente (respuesta binaria)
          headers:
            Content-Disposition:
              description: Nombre de archivo sugerido basado en el nombre del schema
              schema:
                type: string
                example: 'attachment; filename="Leads de Ventas.xlsx"'
          content:
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
        '400':
          description: Petición inválida (datos JSON o schema_id incorrectos/faltantes)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: El schema_id no existe o no pertenece a la cuenta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: No se encontró correspondencia entre claves y schema
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Fallo del servicio de IA encargado de interpretar los datos
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /pdf-json:
    post:
      operationId: pdfToJson
      tags:
        - PDF a JSON
      summary: Extrae datos estructurados de un archivo PDF según un schema
      description: >
        Recibe un archivo .pdf (de texto seleccionable o escaneado) y un
        schema_id, y devuelve un único objeto JSON con los datos extraídos
        del documento completo, con las claves exactamente iguales a las
        propiedades definidas en el schema. A diferencia de excel-json, el
        documento se trata como una única fuente de datos: la respuesta
        siempre contiene exactamente un registro en "data", nunca una fila
        por cada elemento del documento. El tamaño máximo de archivo admitido
        es 15 MB.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PdfJsonRequest'
      responses:
        '200':
          description: PDF analizado y datos extraídos correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PdfJsonSuccessResponse'
        '400':
          description: >
            Petición inválida (archivo faltante, no es un PDF válido, PDF
            vacío, o schema_id incorrecto/faltante/de tipo equivocado)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: El schema_id no existe o no pertenece a la cuenta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: El PDF supera el tamaño máximo admitido (15 MB)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >
            No se pudo extraer ningún dato del PDF relacionado con el schema
            indicado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Fallo del servicio de IA encargado de analizar el PDF
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /doc-json:
    post:
      operationId: docToJson
      tags:
        - Documento a JSON
      summary: Extrae datos estructurados de un documento de texto según un schema
      description: >
        Recibe un archivo de documento (.docx, .txt, .md o .rtf) y un
        schema_id, y devuelve un único objeto JSON con los datos extraídos
        del documento completo, con las claves exactamente iguales a las
        propiedades definidas en el schema. El texto se extrae de forma
        determinista en el servidor ANTES de llamar a la IA (desempaquetado
        del .docx, decodificación UTF-8 para .txt/.md, stripping de códigos
        de control para .rtf); solo el texto plano resultante se envía al
        modelo. El formato legacy .doc (Word 97-2003) no está soportado.
        Igual que en pdf-json, el documento se trata como una única fuente
        de datos: la respuesta siempre contiene exactamente un registro en
        "data". El tamaño máximo de archivo admitido es 10 MB, y el texto
        extraído no puede superar los 300.000 caracteres.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/DocJsonRequest'
      responses:
        '200':
          description: Documento analizado y datos extraídos correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocJsonSuccessResponse'
        '400':
          description: >
            Petición inválida (archivo faltante, formato no soportado,
            documento vacío o sin texto extraíble, .doc legacy sin
            convertir, .docx corrupto, o schema_id incorrecto/faltante/de
            tipo equivocado)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: El schema_id no existe o no pertenece a la cuenta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: >
            El archivo supera el tamaño máximo admitido (10 MB), o el texto
            extraído supera el límite de 300.000 caracteres
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >
            No se pudo extraer ningún dato del documento relacionado con el
            schema indicado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Fallo del servicio de IA encargado de analizar el texto
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /img-json:
    post:
      operationId: imgToJson
      tags:
        - Imagen a JSON
      summary: Extrae datos estructurados de una imagen según un schema
      description: >
        Recibe una imagen (.jpeg, .jpg, .png, .webp, .heic o .heif) y un
        schema_id, y devuelve un único objeto JSON con los datos extraídos
        del contenido visible, con las claves exactamente iguales a las
        propiedades definidas en el schema. El análisis lo realiza un modelo
        multimodal de IA. Antes de extraer datos, el modelo evalúa si la
        imagen es legible (nitidez, enfoque, iluminación); si no lo es,
        responde con 422. Igual que en pdf-json, la imagen se trata como una
        única fuente de datos: la respuesta siempre contiene exactamente un
        registro en "data". El tamaño máximo de archivo admitido es 15 MB.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ImgJsonRequest'
      responses:
        '200':
          description: Imagen analizada y datos extraídos correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImgJsonSuccessResponse'
        '400':
          description: >
            Petición inválida (archivo faltante, formato no soportado,
            imagen vacía, corrupta, firma binaria incoherente, o
            schema_id incorrecto/faltante/de tipo equivocado)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: El schema_id no existe o no pertenece a la cuenta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: La imagen supera el tamaño máximo admitido (15 MB)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >
            Imagen ilegible (borrosa, desenfocada, mal iluminada) o sin
            datos extraíbles según el schema indicado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Fallo del servicio de IA encargado de analizar la imagen
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /agent/excel-json:
    servers:
      - url: https://claix.dev
        description: Base pública Modo Agente
    post:
      operationId: agentExcelToJson
      tags:
        - Modo Agente
      summary: Excel/CSV a JSON con extracción y razonamiento Modo Agente
      description: >
        Equivalente a POST /api/excel-json seguido de una fase agente con Gemini.
        Devuelve la respuesta de extracción (data, mapa_columnas, etc.) más
        agent_data según agent_definition. El schema debe tener is_agent_mode
        activado. Misma petición multipart (file + schema_id) y mismos códigos
        de error que /api/excel-json; además puede devolver 400 si el schema no
        tiene Modo Agente o agent_definition inválida, o 502 si falla la fase
        agente.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ExcelJsonRequest'
      responses:
        '200':
          description: Extracción y fase agente completadas correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentExcelJsonSuccessResponse'
        '400':
          description: >
            Petición inválida, schema sin Modo Agente activado, o
            agent_definition inválida
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: El schema_id no existe o no pertenece a la cuenta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: No se encontró correspondencia entre columnas y schema
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: >
            Fallo del servicio de IA (extracción o fase agente Gemini)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /agent/pdf-json:
    servers:
      - url: https://claix.dev
        description: Base pública Modo Agente
    post:
      operationId: agentPdfToJson
      tags:
        - Modo Agente
      summary: PDF a JSON con extracción y razonamiento Modo Agente
      description: >
        Equivalente a POST /api/pdf-json más fase agente. Devuelve data[] con la
        extracción del schema principal y agent_data con evaluaciones tipadas
        (booleanos, enteros, strings, opciones cerradas). Requiere
        is_agent_mode en el schema. Tamaño máximo del PDF: 15 MB.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PdfJsonRequest'
      responses:
        '200':
          description: Extracción y fase agente completadas correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentPdfJsonSuccessResponse'
        '400':
          description: >
            Petición inválida, schema sin Modo Agente, o agent_definition
            inválida
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: El schema_id no existe o no pertenece a la cuenta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: El PDF supera el tamaño máximo admitido (15 MB)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >
            No se pudo extraer datos del PDF o imagen ilegible en fase previa
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Fallo del servicio de IA (extracción o fase agente)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /agent/doc-json:
    servers:
      - url: https://claix.dev
        description: Base pública Modo Agente
    post:
      operationId: agentDocToJson
      tags:
        - Modo Agente
      summary: Documento a JSON con extracción y razonamiento Modo Agente
      description: >
        Equivalente a POST /api/doc-json más fase agente. Devuelve data[] y
        agent_data. Requiere is_agent_mode. Formatos admitidos: .docx, .txt,
        .md, .rtf. Tamaño máximo: 10 MB; texto extraído máx. 300.000 caracteres.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/DocJsonRequest'
      responses:
        '200':
          description: Extracción y fase agente completadas correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentDocJsonSuccessResponse'
        '400':
          description: >
            Petición inválida, schema sin Modo Agente, o agent_definition
            inválida
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: El schema_id no existe o no pertenece a la cuenta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: Archivo o texto extraído supera límites admitidos
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: No se pudo extraer datos del documento
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Fallo del servicio de IA (extracción o fase agente)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /agent/img-json:
    servers:
      - url: https://claix.dev
        description: Base pública Modo Agente
    post:
      operationId: agentImgToJson
      tags:
        - Modo Agente
      summary: Imagen a JSON con extracción y razonamiento Modo Agente
      description: >
        Equivalente a POST /api/img-json más fase agente. Devuelve data[] y
        agent_data. Requiere is_agent_mode. Formatos: JPEG, PNG, WebP, HEIC/HEIF.
        Tamaño máximo: 15 MB.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ImgJsonRequest'
      responses:
        '200':
          description: Extracción y fase agente completadas correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentImgJsonSuccessResponse'
        '400':
          description: >
            Petición inválida, schema sin Modo Agente, o agent_definition
            inválida
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: El schema_id no existe o no pertenece a la cuenta
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: La imagen supera el tamaño máximo admitido (15 MB)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Imagen ilegible o sin datos extraíbles
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Fallo del servicio de IA (extracción o fase agente)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /schemas:
    get:
      operationId: listSchemas
      tags:
        - Schemas
      summary: Devuelve todos los schemas de la cuenta asociada al API key
      description: >
        Endpoint de solo lectura, sin body ni parámetros. Dado un API key
        válido, devuelve la lista completa de schemas creados por el
        usuario dueño de esa key, con toda su información (id, nombre,
        tipo y definición), ordenados del más reciente al más antiguo. No
        genera ningún registro en usage_logs, ya que no es un endpoint de
        procesamiento/conversión facturable.
      responses:
        '200':
          description: Lista de schemas obtenida correctamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SchemasListResponse'
        '401':
          description: No autorizado (API key inválida, inactiva o cuenta suspendida)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '405':
          description: Método HTTP no permitido (solo se admite GET)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Error interno del servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >
        API key secreta de servidor. Tiene prioridad sobre Authorization
        si se envían ambos headers.
    BearerAuth:
      type: http
      scheme: bearer
      description: Forma alternativa de enviar la API key como Bearer token.

  schemas:
    ExcelJsonRequest:
      type: object
      required:
        - file
        - schema_id
      properties:
        file:
          type: string
          format: binary
          description: Archivo Excel (.xlsx) o CSV (.csv) a transformar.
        schema_id:
          type: string
          format: uuid
          description: >
            Identificador del schema (previamente creado) de tipo
            "Excel/CSV a JSON".
          example: 8f14e45f-ceea-4e6f-8b23-1e2d3c4b5a6f

    ExcelJsonSuccessResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        schema_utilizado:
          type: string
          description: Nombre del schema aplicado.
          example: Leads de Ventas
        total_filas_procesadas:
          type: integer
          description: Número de filas de datos transformadas.
          example: 247
        mapa_columnas:
          type: object
          description: >
            Diccionario que muestra qué columna original se emparejó con
            qué propiedad del schema.
          additionalProperties:
            type: string
          example:
            Nom_cliente: nombre_completo
            Tlf: telefono_movil
            mail de contacto: email_contacto
        data:
          type: array
          description: Registros transformados según el schema.
          items:
            type: object
            additionalProperties: true
          example:
            - nombre_completo: Ana María Gómez
              cargo: CEO & Founder
              empresa: TechSolutions
              email_contacto: ana.gomez@techsolutions.com
              telefono_movil: "+1 (555) 019-2231"

    JsonExcelMultipartRequest:
      type: object
      required:
        - schema_id
      properties:
        schema_id:
          type: string
          format: uuid
          description: >
            Identificador del schema (previamente creado) de tipo
            "JSON a Excel".
          example: 1f9e6103-9221-4c22-8a3a-8592d8b0eb38
        files:
          type: array
          description: >
            Uno o varios archivos o campos de texto con contenido JSON
            (objeto único o array de objetos). Se puede repetir este campo
            o usar nombres de campo distintos; todos los que contengan JSON
            válido son tenidos en cuenta.
          items:
            type: string
            format: binary

    JsonExcelArrayRequest:
      type: array
      description: Forma B.1 -- array directo de registros.
      items:
        type: object
        additionalProperties: true
      example:
        - nombre: Laura Fernández
          email: laura@nebulatech.com
        - nombre: Miguel Gómez
          email: m.gomez@construred.es

    JsonExcelSingleObjectRequest:
      type: object
      description: Forma B.2 -- un único objeto de registro.
      additionalProperties: true
      example:
        nombre: Laura Fernández
        email: laura@nebulatech.com

    JsonExcelEnvelopeRequest:
      type: object
      description: >
        Forma B.3 -- "sobre" con el schema_id incluido y los datos dentro
        de "data" (o, de forma equivalente, "records").
      required:
        - schema_id
        - data
      properties:
        schema_id:
          type: string
          format: uuid
          example: 1f9e6103-9221-4c22-8a3a-8592d8b0eb38
        data:
          type: array
          items:
            type: object
            additionalProperties: true
          example:
            - nombre: Laura Fernández
              email: laura@nebulatech.com
            - nombre: Miguel Gómez
              email: m.gomez@construred.es
        records:
          type: array
          description: Alternativa equivalente a "data".
          items:
            type: object
            additionalProperties: true

    PdfJsonRequest:
      type: object
      required:
        - file
        - schema_id
      properties:
        file:
          type: string
          format: binary
          description: >
            Archivo PDF a analizar (texto seleccionable o escaneado).
            Tamaño máximo admitido: 15 MB.
        schema_id:
          type: string
          format: uuid
          description: >
            Identificador del schema (previamente creado) de tipo
            "PDF a JSON".
          example: 3c7a9f21-4b8e-4d1a-9c6f-2e0d8a5b7c4f

    PdfJsonSuccessResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        schema_utilizado:
          type: string
          description: Nombre del schema aplicado.
          example: Facturas de Proveedores
        total_registros:
          type: integer
          description: >
            Siempre 1 en este endpoint: el documento completo se trata como
            una única fuente de datos, no como una tabla de múltiples filas.
          example: 1
        data:
          type: array
          description: >
            Contiene exactamente un objeto con los datos extraídos del PDF,
            según las propiedades definidas en el schema. Los datos no
            encontrados en el documento se devuelven como null.
          items:
            type: object
            additionalProperties: true
          example:
            - numero_factura: F-2026-00456
              fecha_emision: "2026-03-14"
              proveedor: Suministros Industriales del Ebro S.L.
              importe_total: 1284.50
              moneda: EUR

    DocJsonRequest:
      type: object
      required:
        - file
        - schema_id
      properties:
        file:
          type: string
          format: binary
          description: >
            Archivo de documento a analizar. Formatos admitidos: .docx
            (application/vnd.openxmlformats-officedocument.wordprocessingml.document),
            .txt (text/plain), .md (text/markdown) y .rtf (application/rtf).
            El formato legacy .doc (application/msword) NO está soportado.
            Tamaño máximo admitido: 10 MB.
        schema_id:
          type: string
          format: uuid
          description: >
            Identificador del schema (previamente creado) de tipo
            "Documento a JSON".
          example: b980cfe7-61ef-4a5a-9724-881c8a5541e2

    DocJsonSuccessResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        schema_utilizado:
          type: string
          description: Nombre del schema aplicado.
          example: Contratos de Alquiler
        total_registros:
          type: integer
          description: >
            Siempre 1 en este endpoint: el documento completo se trata como
            una única fuente de datos, no como una tabla de múltiples filas.
          example: 1
        data:
          type: array
          description: >
            Contiene exactamente un objeto con los datos extraídos del
            documento, según las propiedades definidas en el schema. Los
            datos no encontrados en el texto se devuelven como null.
          items:
            type: object
            additionalProperties: true
          example:
            - nombre_arrendatario: Laura Fernández Ruiz
              nombre_arrendador: Inversiones Delta S.L.
              direccion_inmueble: Calle Mayor 14, 3ºB, Madrid
              renta_mensual: 950.00
              fecha_inicio: "2026-04-01"

    ImgJsonRequest:
      type: object
      required:
        - file
        - schema_id
      properties:
        file:
          type: string
          format: binary
          description: >
            Imagen a analizar. Formatos admitidos: .jpeg, .jpg, .png, .webp,
            .heic y .heif. Tamaño máximo admitido: 15 MB.
        schema_id:
          type: string
          format: uuid
          description: >
            Identificador del schema (previamente creado) de tipo
            "Imagen a JSON".
          example: 3c7a9f21-4b8e-4d1a-9c6f-2e0d8a5b7c4f

    ImgJsonSuccessResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        schema_utilizado:
          type: string
          description: Nombre del schema aplicado.
          example: Facturas de Proveedores
        total_registros:
          type: integer
          description: >
            Siempre 1 en este endpoint: la imagen completa se trata como
            una única fuente de datos, no como una tabla de múltiples filas.
          example: 1
        data:
          type: array
          description: >
            Contiene exactamente un objeto con los datos extraídos de la
            imagen, según las propiedades definidas en el schema. Los datos
            no encontrados se devuelven como null.
          items:
            type: object
            additionalProperties: true
          example:
            - numero_factura: F-2026-00456
              fecha_emision: "2026-03-14"
              proveedor: Suministros Industriales del Ebro S.L.
              importe_total: 1284.50
              moneda: EUR

    AgentData:
      type: object
      description: >
        Respuestas tipadas de la fase Modo Agente según agent_definition del
        schema. Las claves coinciden con los nombres definidos en el dashboard;
        los tipos dependen de cada campo (boolean, integer, string u opción
        cerrada). Opcionalmente puede incluir resumen_agent como string si está
        configurado en el schema.
      additionalProperties: true
      example:
        clausula_penalizacion: true
        salario: 55000
        es_parcial: false
        resumen_contrato: "Contrato indefinido con jornada completa."

    AgentExcelJsonSuccessResponse:
      allOf:
        - $ref: '#/components/schemas/ExcelJsonSuccessResponse'
        - type: object
          required:
            - agent_data
          properties:
            agent_data:
              $ref: '#/components/schemas/AgentData'

    AgentPdfJsonSuccessResponse:
      allOf:
        - $ref: '#/components/schemas/PdfJsonSuccessResponse'
        - type: object
          required:
            - agent_data
          properties:
            agent_data:
              $ref: '#/components/schemas/AgentData'

    AgentDocJsonSuccessResponse:
      allOf:
        - $ref: '#/components/schemas/DocJsonSuccessResponse'
        - type: object
          required:
            - agent_data
          properties:
            agent_data:
              $ref: '#/components/schemas/AgentData'

    AgentImgJsonSuccessResponse:
      allOf:
        - $ref: '#/components/schemas/ImgJsonSuccessResponse'
        - type: object
          required:
            - agent_data
          properties:
            agent_data:
              $ref: '#/components/schemas/AgentData'

    AgentFieldDefinition:
      type: object
      required:
        - type
        - description
      properties:
        type:
          type: string
          enum:
            - boolean
            - string
            - closed
            - integer
          description: Tipo de respuesta esperada para este campo agente.
        description:
          type: string
          description: Instrucción semántica para el modelo en la fase agente.
        options:
          type: array
          items:
            type: string
          description: >
            Valores permitidos cuando type es "closed"; el modelo debe devolver
            exactamente una de estas opciones.

    SchemaItem:
      type: object
      description: Un schema tal como está almacenado en la cuenta.
      properties:
        id:
          type: string
          format: uuid
          example: b980cfe7-61ef-4a5a-9724-881c8a5541e2
        name:
          type: string
          example: Facturas Trimestrales
        type:
          type: string
          description: >
            Dirección de conversión para la que está pensado este schema.
          enum:
            - excel-json
            - json-excel
            - pdf-json
            - doc-json
            - img-json
          example: pdf-json
        schema_definition:
          type: object
          description: >
            Definición de las propiedades del schema (jsonb), cada una con
            su "type" y, opcionalmente, una "description" que ayuda al
            reconocimiento semántico en los endpoints de conversión.
          additionalProperties: true
          example:
            nif_cliente:
              type: string
              description: NIF o CIF del cliente facturado.
        is_agent_mode:
          type: boolean
          description: >
            Si es true, el schema puede usarse con los endpoints /agent/*-json.
          example: false
        agent_definition:
          type: object
          description: >
            Definición de campos para la fase Modo Agente (solo relevante si
            is_agent_mode es true). Cada clave es el nombre del campo en
            agent_data.
          additionalProperties:
            $ref: '#/components/schemas/AgentFieldDefinition'
          nullable: true
          example:
            clausula_penalizacion:
              type: boolean
              description: Indica si el contrato incluye cláusula de penalización.
        resumen_agent:
          type: string
          nullable: true
          maxLength: 500
          description: >
            Texto opcional con instrucciones adicionales para el resumen o
            razonamiento del Modo Agente.
          example: null
        created_at:
          type: string
          format: date-time
          example: "2026-08-06T09:51:41.372964+00:00"

    SchemasListResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        total_schemas:
          type: integer
          description: Número total de schemas devueltos.
          example: 5
        schemas:
          type: array
          items:
            $ref: '#/components/schemas/SchemaItem'

    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Descripción legible del problema.
          example: No se envió el campo schema_id.
        detalle:
          type: string
          description: Información técnica adicional (solo presente en algunos casos).
          example: "Missing required field 'schema_id' in multipart/form-data body."
