openapi: 3.0.3
info:
  title: Veracitas API
  description: |
    API pública y B2B de Veracitas (veracitas.es).
    Público: GET /api/verify. B2B: /api/v1/submissions (Bearer o X-Api-Key).
    Errores JSON con code, message, hint, docs y openapi.
    Spec canónica: https://veracitas.es/openapi.json
  version: "1.0.0"
  contact:
    name: Veracitas — Ready For Metaverse
    email: contacto@veracitas.es
    url: https://veracitas.es/developers
  license:
    name: Uso del servicio según aviso legal
    url: https://veracitas.es/legal
servers:
  - url: https://veracitas.es
    description: Producción
tags:
  - name: public
    description: Verificación pública de un Sello Veracitas. Sin autenticación.
  - name: b2b
    description: Envíos de una cuenta de portal. Requiere token Bearer.
paths:
  /api/verify:
    get:
      operationId: verifySeal
      tags: [public]
      summary: Verificar un sello por query
      description: Estado público de un Sello Veracitas. Sin auth. El informe técnico se recorta si no hay full_access.
      parameters:
        - name: cert_id
          in: query
          required: true
          description: Identificador UUIDv4 del sello.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Sello encontrado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerifySuccess"
        "400":
          description: cert_id no es un UUID.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: No hay sello con ese UUID.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "405":
          description: Método distinto de GET.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /api/verify/{cert_id}:
    get:
      operationId: verifySealByPath
      tags: [public]
      summary: Verificar un sello por ruta
      description: Igual que verifySeal, con el UUID en la ruta. Preferida para agentes.
      parameters:
        - name: cert_id
          in: path
          required: true
          description: Identificador UUIDv4 del sello.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Sello encontrado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerifySuccess"
        "400":
          description: UUID inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Sello no encontrado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /api/v1/submissions:
    get:
      operationId: listSubmissions
      tags: [b2b]
      summary: Listar envíos de la cuenta
      description: Lista los envíos del perfil autenticado. limit máximo 200.
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      parameters:
        - name: status
          in: query
          schema: { type: string, pattern: "^[a-z_]+$" }
        - name: plan
          in: query
          schema: { type: string, enum: [basic, premium] }
        - name: q
          in: query
          schema: { type: string }
        - name: sort
          in: query
          schema: { type: string, enum: [date, words], default: date }
        - name: order
          in: query
          schema: { type: string, enum: [asc, desc], default: desc }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
        - name: offset
          in: query
          schema: { type: integer, minimum: 0, default: 0 }
      responses:
        "200":
          description: Listado paginado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubmissionList"
        "401":
          description: Token ausente o inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
    post:
      operationId: createSubmission
      tags: [b2b]
      summary: Crear un envío
      description: Sube un manuscrito (multipart) y lanza el análisis. txt/pdf/doc/docx. Mínimo 1.500 palabras.
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                  description: Manuscrito, máximo 50 MB.
                title: { type: string }
                author: { type: string }
                author_email: { type: string, format: email }
                genre: { type: string }
                plan: { type: string, enum: [basic, premium], default: basic }
      responses:
        "201":
          description: Envío creado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubmissionCreated"
        "400":
          description: Falta el archivo.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Token inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "413":
          description: Archivo demasiado grande.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "429":
          description: Cuota o rate limit.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
  /api/v1/submissions/{sid}:
    get:
      operationId: getSubmission
      tags: [b2b]
      summary: Estado de un envío
      description: Detalle de un envío de la cuenta autenticada.
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      parameters:
        - name: sid
          in: path
          required: true
          description: UUIDv4 del envío.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Envío encontrado.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubmissionDetail"
        "400":
          description: sid inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "401":
          description: Token inválido.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "404":
          description: Envío no encontrado en este perfil.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API token
      description: Token del portal. Authorization Bearer.
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-Api-Key
      description: Alternativa al Bearer.
  schemas:
    ApiError:
      type: object
      required: [ok, error, message, status, code, hint]
      properties:
        ok: { type: boolean, enum: [false] }
        error: { type: string }
        message: { type: string }
        status: { type: integer }
        code: { type: string }
        hint: { type: string }
        docs: { type: string, format: uri }
        openapi: { type: string, format: uri }
        timestamp: { type: string, format: date-time }
        path: { type: string }
        cert_id: { type: string, format: uuid }
    VerifySuccess:
      type: object
      required: [ok, verified, cert_id, seal_status]
      properties:
        ok: { type: boolean }
        verified: { type: boolean }
        full_access: { type: boolean }
        cert_id: { type: string, format: uuid }
        seal_status: { type: string }
        text_sha256: { type: string, nullable: true }
        summary: { type: object, additionalProperties: true }
        timestamp: { type: string, format: date-time }
    Submission:
      type: object
      required: [sid]
      properties:
        sid: { type: string, format: uuid }
        title: { type: string }
        analysis_status: { type: string }
        cert_id: { type: string, format: uuid, nullable: true }
        verify_url_public: { type: string, format: uri, nullable: true }
    SubmissionList:
      type: object
      properties:
        ok: { type: boolean }
        total: { type: integer }
        submissions:
          type: array
          items:
            $ref: "#/components/schemas/Submission"
    SubmissionDetail:
      type: object
      properties:
        ok: { type: boolean }
        submission:
          $ref: "#/components/schemas/Submission"
    SubmissionCreated:
      type: object
      properties:
        ok: { type: boolean }
        submission:
          type: object
          properties:
            sid: { type: string, format: uuid }
            status_url: { type: string, format: uri }
