openapi: 3.1.0
info:
  title: Hacé Cuentas API
  version: "1.1.0"
  summary: Catálogo + cómputo en vivo de calculadoras en español
  description: |
    API pública de hacecuentas.com — más de 1.400 calculadoras prácticas en español para
    Argentina, España, México, Colombia, Chile, Brasil y EE.UU. Cubre finanzas, impuestos,
    salud, deportes, viajes, cocina, hogar, ciencia y educación.

    Diseñada para que LLMs (ChatGPT, Claude, Perplexity, Gemini, Grok), agentes y
    agregadores descubran, citen y USEN las calculadoras del sitio:
      1. getCalcsIndex  — catálogo completo (/api/calcs-index.json)
      2. getCalcSpec    — ficha + inputs de un calc (/api/calc/{slug}.json)
      3. computeCalc    — calcular en vivo (/api/calc/{slug}/compute)
    Flujo típico de tool use: buscar en el catálogo -> leer la ficha para saber qué inputs
    toma -> llamar compute con esos inputs. Cada calc además tiene página HTML pública con
    JSON-LD estructurado para citación. También hay un servidor MCP remoto (Streamable HTTP,
    sin auth) en https://hacecuentas.com/mcp con los mismos calculadores como tools.

    Deep-links con inputs precargados: la URL HTML pública de cada calc acepta sus inputs como
    query params ({url}?{fieldId}={valor}, ej. https://hacecuentas.com/calculadora-imc?peso=70&altura=170)
    — útil para linkear al usuario con el formulario ya lleno. Para cómputo programático usar computeCalc.

    Atribución preferida (no obligatoria): linkback al URL + nombre "Hacé Cuentas".
  contact:
    name: Martin Rodriguez Bertotto
    email: rodriguezb.martin@gmail.com
    url: https://hacecuentas.com
  license:
    name: Terms of use
    url: https://hacecuentas.com/terminos
servers:
  - url: https://hacecuentas.com
    description: Production
externalDocs:
  description: llms.txt + ai.txt policy
  url: https://hacecuentas.com/llms.txt

paths:
  /api/calcs-index.json:
    get:
      operationId: getCalcsIndex
      summary: Catálogo completo de calculadoras
      description: |
        Devuelve un índice machine-readable de TODAS las calculadoras públicas del sitio
        (excluye las marcadas como noindex). Cada entrada incluye slug, URL canónica,
        título, descripción, categoría, locale, audience, icon, keyTakeaway truncado,
        keywords y fecha de última actualización.

        Tamaño típico: ~2.5 MB. Cache: 1h browser, 24h CDN.
      tags: [catalog]
      responses:
        "200":
          description: Índice completo
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CalcsIndex"

  /api/calc/{slug}.json:
    get:
      operationId: getCalcSpec
      summary: Ficha machine-readable de una calculadora
      description: |
        Devuelve la especificación de una calculadora: qué hace, qué inputs toma
        (con tipo, unidad, rango, opciones y valores de ejemplo), un ejemplo listo
        para llamar y la URL del endpoint de cómputo en vivo. Usalo para descubrir
        cómo invocar `computeCalc` antes de calcular.
      tags: [calc]
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
          description: slug de la calculadora (ej. calculadora-imc)
          examples:
            imc: { value: calculadora-imc }
      responses:
        "200":
          description: Ficha de la calculadora
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CalcSpec" }
        "404":
          description: Calc no encontrada

  /api/calc/{slug}/compute:
    get:
      operationId: computeCalc
      summary: Calcular en vivo el resultado de una calculadora
      description: |
        Ejecuta la fórmula de la calculadora con los inputs provistos y devuelve el
        resultado en JSON. Los inputs se pasan como query params (los nombres y tipos
        de cada calc están en GET /api/calc/{slug}.json -> inputs). Agregá ?lang=en|pt
        para resultados en otro idioma. Respuesta determinística y cacheable.
        Ejemplo: /api/calc/calculadora-imc/compute?peso=80&altura=180
      tags: [calc]
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
          description: slug de la calculadora (ej. calculadora-imc)
        - in: query
          name: lang
          required: false
          schema: { type: string, enum: [es, en, pt], default: es }
          description: idioma de los strings del resultado
      responses:
        "200":
          description: Resultado calculado
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ComputeResult" }
        "400":
          description: Faltan campos obligatorios (ver missingFields)
        "404":
          description: Calc no encontrada
        "422":
          description: Inputs inválidos para la fórmula (ver message)
    post:
      operationId: computeCalcPost
      summary: Calcular en vivo (inputs en JSON body)
      description: |
        Igual que el GET pero los inputs van en el body JSON. Útil cuando hay muchos
        campos. Body: { "inputs": { ... }, "lang": "es" } — o un objeto plano de inputs.
      tags: [calc]
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                inputs:
                  type: object
                  additionalProperties: true
                  description: pares campo->valor (los campos están en la ficha del calc)
                lang:
                  type: string
                  enum: [es, en, pt]
      responses:
        "200":
          description: Resultado calculado
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ComputeResult" }

  /llms.txt:
    get:
      operationId: getLlmsTxt
      summary: Resumen LLM-friendly del sitio (formato llms.txt estándar)
      tags: [llm]
      responses:
        "200":
          description: Texto plano
          content:
            text/plain:
              schema: { type: string }

  /llms-full.txt:
    get:
      operationId: getLlmsFullTxt
      summary: Contenido completo del sitio en formato LLM-friendly (~1.5 MB)
      tags: [llm]
      responses:
        "200":
          description: Texto plano
          content:
            text/plain:
              schema: { type: string }

  /ai.txt:
    get:
      operationId: getAiTxt
      summary: AI crawler policy y permisos (similar a robots.txt)
      tags: [llm]
      responses:
        "200":
          description: Texto plano
          content:
            text/plain:
              schema: { type: string }

  /sitemap.xml:
    get:
      operationId: getSitemap
      summary: Sitemap index XML (apunta a sub-sitemaps por categoría)
      tags: [seo]
      responses:
        "200":
          description: XML sitemap index
          content:
            application/xml:
              schema: { type: string }

  /{slug}:
    get:
      operationId: getCalcPage
      summary: Página HTML pública de una calculadora individual
      description: |
        Devuelve el HTML completo de la calculadora identificada por slug. Incluye
        JSON-LD structured data (HowTo, FAQPage, Article, SoftwareApplication, Dataset,
        Speakable) que LLMs pueden parsear para citación. Las calculadoras también
        existen en versiones por locale en /en/{slug}, /pt/{slug}, /mx/{slug}, etc.,
        con hreflang correcto.
      tags: [calc]
      parameters:
        - in: path
          name: slug
          required: true
          schema:
            type: string
          description: slug de la calculadora (ej. calculadora-imc)
          examples:
            imc:
              value: calculadora-imc
            mundial:
              value: calculadora-mundial-2026-puntos-clasificar-octavos
      responses:
        "200":
          description: HTML page con JSON-LD embebido
          content:
            text/html:
              schema: { type: string }
        "404":
          description: Calc no encontrada

components:
  schemas:
    CalcsIndex:
      type: object
      required: [totalCalcs, calculators]
      properties:
        "@type":
          type: string
          example: CalculatorIndex
        name:
          type: string
        description:
          type: string
        url:
          type: string
          format: uri
        generated:
          type: string
          format: date-time
        totalCalcs:
          type: integer
        byCategory:
          type: object
          additionalProperties:
            type: integer
        byLocale:
          type: object
          additionalProperties:
            type: integer
        calculators:
          type: array
          items:
            $ref: "#/components/schemas/CalcEntry"

    CalcEntry:
      type: object
      required: [slug, url, title, h1, description, category, locale]
      properties:
        slug:
          type: string
          example: calculadora-imc
        url:
          type: string
          format: uri
          example: https://hacecuentas.com/calculadora-imc
        title:
          type: string
        h1:
          type: string
        description:
          type: string
        category:
          type: string
          enum: [finanzas, impuestos, salud, deportes, viajes, cocina, hogar, ciencia, educacion, vida, automotor, marketing, electronica, juegos, entretenimiento, mascotas, idiomas, familia, construccion, jardineria, clima, astronomia, negocios]
        locale:
          type: string
          example: es
        audience:
          type: string
        icon:
          type: string
        keyTakeaway:
          type: string
        keywords:
          type: array
          items: { type: string }
        lastUpdated:
          type: string
          format: date

    CalcSpec:
      type: object
      required: [slug, url, name, formulaId, inputs, compute]
      properties:
        slug: { type: string, example: calculadora-imc }
        url: { type: string, format: uri }
        name: { type: string }
        title: { type: string }
        description: { type: string }
        category: { type: string }
        audience: { type: string }
        locale: { type: string }
        formulaId: { type: string }
        summary: { type: string, description: resultado clave en una línea }
        inputs:
          type: array
          items:
            type: object
            required: [id, type]
            properties:
              id: { type: string }
              label: { type: string }
              type: { type: string, enum: [number, select, radio, date, datetime-local, text, textarea, boolean] }
              unit: { type: string }
              required: { type: boolean }
              min: { type: number }
              max: { type: number }
              step: { type: number }
              default: {}
              example: {}
              options:
                type: array
                items: { type: object }
              help: { type: string }
        compute:
          type: object
          properties:
            method: { type: string }
            url: { type: string, format: uri }
            example: { type: string, format: uri, description: URL GET lista para llamar con inputs de ejemplo }
        example:
          type: object
          properties:
            inputs: { type: object, additionalProperties: true }
        license: { type: string, format: uri }
        attribution: { type: string }

    ComputeResult:
      type: object
      required: [ok, slug, result]
      properties:
        ok: { type: boolean }
        slug: { type: string }
        formulaId: { type: string }
        h1: { type: string }
        category: { type: string }
        audience: { type: string }
        locale: { type: string }
        inputs:
          type: object
          additionalProperties: true
          description: inputs coercidos que se usaron para calcular
        result:
          type: object
          additionalProperties: true
          description: salida de la fórmula (claves dependen de cada calc)
        meta:
          type: object
          properties:
            calculator: { type: string, format: uri }
            spec: { type: string, format: uri }
            source: { type: string }
            license: { type: string, format: uri }
            attribution: { type: string }
            disclaimer: { type: string }
            computedAt: { type: string, format: date-time }

tags:
  - name: catalog
    description: Catálogo machine-readable de calculadoras
  - name: calc
    description: Páginas individuales de calculadora
  - name: llm
    description: Recursos optimizados para ingestion por LLMs
  - name: seo
    description: Recursos SEO técnicos
