{
  "openapi": "3.1.0",
  "info": {
    "title": "kalu API",
    "version": "1.0.0",
    "summary": "Las noticias que afectan a las cuentas de tu radar, con su fuente real, para tu CRM.",
    "description": "API de solo lectura. Cada nota trae la URL de su fuente original; una nota sin fuente verificable no se entrega. La autenticación es una llave de organización (Ajustes → Conectar con tu CRM) enviada como `Authorization: Bearer kalu_live_…`. Límite: 60 llamadas por minuto por llave, y 120 por minuto por organización sumando todas sus llaves y el servidor MCP. Incluida en los paquetes Comercial Plus y Corporativo. Guía: https://kalu.lat/integraciones",
    "contact": { "name": "kalu", "url": "https://kalu.lat/integraciones" }
  },
  "servers": [
    { "url": "https://rernregrsxvgtgmscdvf.supabase.co/functions/v1/api-v1/v1", "description": "Producción" }
  ],
  "security": [{ "llave": [] }],
  "paths": {
    "/empresas/{dominio}/noticias": {
      "get": {
        "operationId": "noticiasDeEmpresa",
        "summary": "Las noticias que nombran a una empresa de tu radar",
        "description": "Lo que necesita una tarjeta en la ficha de una cuenta. El dominio se normaliza (sin `www.`, sin ruta). Responde 404 si la empresa no está en el radar de tu organización.",
        "parameters": [
          { "name": "dominio", "in": "path", "required": true, "schema": { "type": "string", "examples": ["grupobimbo.com"] } },
          { "$ref": "#/components/parameters/desde" },
          { "$ref": "#/components/parameters/hasta" },
          { "$ref": "#/components/parameters/categoria" },
          { "$ref": "#/components/parameters/limite" },
          { "$ref": "#/components/parameters/cursor" }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/Pagina" },
          "401": { "$ref": "#/components/responses/Error" },
          "402": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/noticias": {
      "get": {
        "operationId": "noticias",
        "summary": "Todas las noticias de tu organización, por lotes",
        "description": "Para sincronizar. Ordenadas de la más reciente a la más antigua; sigue `next_cursor` hasta que sea null. Sin `desde`, la última semana; nunca más atrás de la ventana de archivo de tu paquete.",
        "parameters": [
          { "$ref": "#/components/parameters/desde" },
          { "$ref": "#/components/parameters/hasta" },
          { "$ref": "#/components/parameters/categoria" },
          { "$ref": "#/components/parameters/limite" },
          { "$ref": "#/components/parameters/cursor" }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/Pagina" },
          "401": { "$ref": "#/components/responses/Error" },
          "402": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/radar": {
      "get": {
        "operationId": "radar",
        "summary": "Las empresas que vigila kalu, con su dominio",
        "description": "Clientes, proveedores y competidores del radar, cada empresa una sola vez: si la misma empresa está cargada varias veces, sale una. `relaciones` trae todos sus tipos (una empresa puede ser cliente y proveedor a la vez) y `relacion` el principal: cliente, luego proveedor, luego competidor. El dominio es la llave para emparejar con las cuentas de tu CRM; los competidores no traen dominio.",
        "responses": {
          "200": {
            "description": "Las empresas del radar.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/EmpresaDelRadar" } } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "402": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "webhooks": {
    "noticia": {
      "post": {
        "summary": "Una noticia hacia tu CRM",
        "description": "kalu hace POST a la URL que guardaste en Ajustes con cada noticia que nombra a una cuenta de tu radar (`noticia.nueva`) y con cada alerta urgente (`alerta.urgente`). Cabeceras: `X-Kalu-Event`, `X-Kalu-Delivery` (única por envío) y `X-Kalu-Signature: t=<unix>,v1=<hex>`, donde v1 es HMAC-SHA256 del secreto de firma sobre `t + \".\" + cuerpo`. Rechaza firmas de más de 5 minutos. Responde 2xx para confirmar; tras 10 fallos seguidos el envío se pausa.",
        "requestBody": {
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Carga" } } }
        },
        "responses": { "200": { "description": "Recibido." } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "llave": { "type": "http", "scheme": "bearer", "description": "Llave de organización `kalu_live_…`. Se crea y se revoca en Ajustes." }
    },
    "parameters": {
      "desde": { "name": "desde", "in": "query", "schema": { "type": "string", "format": "date-time" }, "description": "Desde cuándo (ISO 8601). Por defecto, hace 7 días." },
      "hasta": { "name": "hasta", "in": "query", "schema": { "type": "string", "format": "date-time" } },
      "categoria": {
        "name": "categoria",
        "in": "query",
        "schema": { "type": "string", "enum": ["prospectos", "regulatorio", "industria_propia", "industria_cliente", "riesgo_geografico", "internacional"] }
      },
      "limite": { "name": "limite", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } },
      "cursor": { "name": "cursor", "in": "query", "schema": { "type": "string" }, "description": "El `next_cursor` de la página anterior." }
    },
    "responses": {
      "Pagina": {
        "description": "Una página de noticias.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": ["data", "next_cursor"],
              "properties": {
                "data": { "type": "array", "items": { "$ref": "#/components/schemas/Nota" } },
                "next_cursor": { "type": ["string", "null"] }
              }
            }
          }
        }
      },
      "Error": {
        "description": "Error. 401 llave inválida o revocada; 402 sin suscripción activa; 403 el paquete no incluye la API; 404 no existe; 422 parámetro inválido; 429 límite de llamadas.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": ["error"],
              "properties": {
                "error": {
                  "type": "object",
                  "required": ["code", "message"],
                  "properties": {
                    "code": {
                      "type": "string",
                      "enum": ["llave_invalida", "sin_suscripcion", "paquete_sin_api", "no_encontrado", "parametro_invalido", "metodo_no_permitido", "demasiadas_llamadas", "error_interno"]
                    },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Empresa": {
        "type": "object",
        "required": ["nombre", "dominio", "relacion"],
        "properties": {
          "nombre": { "type": "string" },
          "dominio": { "type": ["string", "null"], "description": "El dominio verificable. Null en competidores." },
          "relacion": { "type": "string", "enum": ["cliente", "proveedor", "competidor"] }
        }
      },
      "EmpresaDelRadar": {
        "allOf": [
          { "$ref": "#/components/schemas/Empresa" },
          {
            "type": "object",
            "required": ["relaciones"],
            "properties": {
              "relaciones": {
                "type": "array",
                "items": { "type": "string", "enum": ["cliente", "proveedor", "competidor"] },
                "description": "Todos los tipos de la empresa en tu radar, empezando por `relacion`."
              }
            }
          }
        ]
      },
      "Nota": {
        "type": "object",
        "required": ["id", "titulo", "resumen", "categoria", "urgente", "publicada", "fuente", "kalu_url", "empresa"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "titulo": { "type": "string" },
          "resumen": { "type": "string" },
          "categoria": { "type": ["string", "null"] },
          "urgente": { "type": "boolean" },
          "publicada": { "type": "string", "format": "date-time" },
          "fuente": {
            "type": "object",
            "required": ["url", "medio"],
            "description": "La publicación original. Siempre presente.",
            "properties": { "url": { "type": "string", "format": "uri" }, "medio": { "type": ["string", "null"] } }
          },
          "kalu_url": { "type": "string", "format": "uri", "description": "La nota en kalu, con su análisis." },
          "empresa": { "oneOf": [{ "$ref": "#/components/schemas/Empresa" }, { "type": "null" }] }
        }
      },
      "Carga": {
        "allOf": [
          { "$ref": "#/components/schemas/Nota" },
          {
            "type": "object",
            "required": ["evento", "version", "enviado"],
            "properties": {
              "evento": { "type": "string", "enum": ["noticia.nueva", "alerta.urgente", "prueba"] },
              "version": { "type": "integer", "const": 1 },
              "enviado": { "type": "string", "format": "date-time" }
            }
          }
        ]
      }
    }
  }
}
