API v1 · Agencia y Corporativo

La inteligencia de LicitIA en tus sistemas

Lleva a Power BI, Excel, tu CRM o tu propio software los procesos de tu radar, tus contratos ganados con sus adiciones, el mercado de tu sector y la ficha de cualquier empresa por NIT. Y recibe cada proceso nuevo en tiempo real por webhook firmado.

La API y los webhooks no usan inteligencia artificial: no consumen los créditos de IA de tu plan. Son datos y avisos; los créditos solo se gastan en la evaluación de elegibilidad, las propuestas, el análisis de riesgos y el asistente.

Inicio rápido

  1. En LicitIA entra a Herramientas → Integraciones y pulsa Crear llave. Cópiala: solo se muestra una vez.
  2. Haz tu primera llamada:
Terminal
curl -H "Authorization: Bearer lic_TU_LLAVE" \
  "https://bqjhmvnlvdpvbxvqtodh.supabase.co/functions/v1/api-v1/v1/cuenta"

La respuesta trae tu plan, tus límites y las categorías de tu radar:

Respuesta
{
  "data": {
    "plan": "Corporativo",
    "limites": { "llamadas_por_minuto": 120, "filas_por_pagina": 1000, "dias_de_procesos": 365, "anios_de_historial": 10 },
    "llave": { "prefijo": "lic_Ab12C3", "alcances": ["lectura"], "empresa_idx": 1, "expira": null },
    "radar": { "categorias": ["consultoría", "obra civil"] }
  }
}

Dirección base de todas las llamadas: https://bqjhmvnlvdpvbxvqtodh.supabase.co/functions/v1/api-v1/v1

Recursos

Todos son GET y responden { "data": …, "meta": … }. Los parámetros y campos de cada uno están en la referencia.

RecursoQué entrega
GET/procesosProcesos publicados que coinciden con tu radar, del último año. Filtros: categoria, departamento, valor_min, desde.
GET/adjudicacionesContratos ganados por tu empresa en SECOP I y II, sin duplicados, con valor inicial, adiciones, prórrogas, suspensiones y cesiones.
GET/mercado?familia=8111Quién gana en una familia UNSPSC (por NIT; consorcios por su código de grupo), cuántos oferentes hay por proceso (mediana) y qué entidades compran.
GET/proveedores/{nit}Ficha de cualquier empresa: contratos por año, entidad y familia, descuento típico, adiciones y prórrogas.
GET/proveedores/{nit}/consorciosConsorcios y uniones temporales donde participa, con su porcentaje y las victorias de cada grupo.
GET/proveedores/{nit}/redContra quién compite: rivales, quién gana a quién y quién oferta más barato (SECOP II desde 2024). Disponible si se consultó en LicitIA en los últimos 7 días.
GET/cuentaTu plan, tus límites y la llave en uso.

Autenticación

Envía la llave en la cabecera Authorization: Bearer lic_… (o X-API-Key: lic_…). Cada llave:

  • queda atada a una empresa de tu grupo: su radar, su NIT y sus contratos;
  • se guarda en LicitIA solo como huella (hash): si la pierdes, revócala y crea otra;
  • puedes tener hasta 5 activas, por ejemplo una por sistema, para revocar una sin tocar las demás.

Solo Power BI «Web básico» admite la llave en la URL (?apikey=lic_…). Evítalo en todo lo demás: la URL queda guardada en historiales y registros.

Paginación y límites

Las listas se piden por páginas con limite y desplazamiento. Cuando hay más, meta.siguiente trae el enlace listo para la próxima página; cuando es null, terminaste.

PlanLlamadas por minutoFilas por páginaHistorial
Agencia60500Procesos: 1 año · Contratos y mercado: 10 años
Corporativo1201.000Procesos: 1 año · Contratos y mercado: 10 años

Cada respuesta trae RateLimit-Limit, RateLimit-Remaining y RateLimit-Reset (segundos). Si te pasas recibes 429 con Retry-After: espera esos segundos y reintenta.

Errores

Los errores llegan como application/problem+json (RFC 9457). Decide por code, que no cambia; el texto de detail sí puede cambiar. Si escribes a soporte, incluye el request_id.

Ejemplo
{
  "type": "https://licitia.com.co/api/errores#limite_excedido",
  "title": "Demasiadas solicitudes",
  "status": 429,
  "detail": "Pasaste de 60 llamadas por minuto. Reintenta en 23 s.",
  "code": "limite_excedido",
  "request_id": "6ac16c2f-05ee-40ae-8839-8e5776776a1e"
}
HTTPcodeQué hacer
400parametro_invalido, familia_requerida, nit_invalido, categoria_fuera_del_radarCorrige el parámetro que indica el error.
401llave_ausente, llave_invalida, llave_revocada, llave_vencidaRevisa la cabecera o crea una llave nueva.
403plan_sin_api, plan_vencido, empresa_fuera_de_alcanceLa cuenta no tiene acceso: revisa el plan o los permisos.
404ruta_no_existe, sin_contratos, red_no_calculadaNo hay datos para eso.
409empresa_sin_nitAñade el NIT de la empresa en su perfil de LicitIA.
429limite_excedidoEspera Retry-After segundos.
502fuente_no_disponibleSECOP no respondió. Reintenta en unos minutos.

Power BI y Excel

Plantilla lista

La forma más rápida: un informe de Power BI con tres páginas (tus contratos, los procesos de tu radar y el mercado de tu sector) que se llena con tu llave.

  1. Descarga LicitIA-PowerBI.pbit y ábrelo con Power BI Desktop.
  2. Te pide los parámetros: la familia UNSPSC (p. ej. 8111), el departamento (NACIONAL o uno), los años del mercado y de tus contratos, y los días de procesos. Luego los cambias en Transformar datos → Editar parámetros.
  3. Cuando pida credenciales elige API web y pega tu llave lic_…. Power BI la guarda en sus credenciales, no en el archivo, así que puedes compartir el informe sin compartir la llave.
  4. Publícalo en el servicio de Power BI y programa la actualización; ahí también eliges API web en las credenciales del conjunto de datos.

¿Prefieres armar tu propio informe o usar Excel? LicitIA-PowerQuery.pq trae las mismas consultas para pegarlas a mano, y el tema de colores de LicitIA se importa en Vista → Temas → Buscar temas.

Consulta a mano

Power BI y Excel usan el mismo lenguaje (Power Query). Esta consulta trae todas las páginas de /procesos y guarda la llave en las credenciales de Power BI, no en el texto de la consulta:

  1. Power BI: Obtener datos → Consulta en blanco (Excel: Datos → Obtener datos → De otras fuentes → Consulta en blanco).
  2. Abre el Editor avanzado y pega:
Power Query (M)
let
    Base = "https://bqjhmvnlvdpvbxvqtodh.supabase.co/functions/v1/api-v1/v1",
    Tamano = 500,
    Pagina = (desp as number) =>
        Json.Document(Web.Contents(Base, [
            RelativePath = "procesos",
            Query = [limite = Number.ToText(Tamano), desplazamiento = Number.ToText(desp)],
            ApiKeyName = "apikey"
        ])),
    Paginas = List.Generate(
        () => [d = 0, r = Pagina(0)],
        each List.Count([r][data]) > 0,
        each [d = [d] + Tamano, r = Pagina([d] + Tamano)],
        each [r][data]),
    Filas = List.Combine(Paginas),
    Tabla = Table.FromRecords(Filas, null, MissingField.UseNull)
in
    Tabla
  1. Cuando pida credenciales elige API web y pega tu llave.
  2. Para otros recursos cambia RelativePath: "adjudicaciones", "mercado" (con familia en Query), "proveedores/900123456".

La consulta se actualiza sola en el servicio de Power BI con la programación de tu conjunto de datos.

Asistentes de IA (MCP)

Conecta tu asistente (Claude, ChatGPT, Copilot, Cursor u otro compatible con MCP) a LicitIA con tu misma llave. El asistente consulta tus procesos, tus contratos en SECOP, el mercado de tu sector y la ficha de cualquier empresa, y lo combina con los documentos que tienes en tu computador: propuestas anteriores, certificaciones, APU.

Tu biblioteca no sale de tu equipo. El servidor MCP de LicitIA solo entrega datos de LicitIA; no recibe, no lee ni guarda tus documentos. Y la IA corre en tu asistente, así que no consume créditos de LicitIA.

Dirección del servidor
https://bqjhmvnlvdpvbxvqtodh.supabase.co/functions/v1/mcp

Herramientas

HerramientaQué hace
buscar_procesosProcesos de tu radar, con filtros de categoría, departamento, valor y fecha.
mis_contratosTus contratos en SECOP I y II, con adiciones y prórrogas.
mercado_sectorQuién gana en una familia UNSPSC, descuento típico y entidades que más contratan.
ficha_empresaContratos de cualquier empresa por NIT.
consorcios_empresaConsorcios y uniones temporales de una empresa, con su %.
red_competidoresContra quién compite una empresa (si la red se calculó en LicitIA en los últimos 7 días).
licitia_cuentaTu plan, límites y categorías del radar.

Trae además dos conversaciones listas: ¿Me sirve este proceso? (compara un proceso con tu experiencia de SECOP y de tus documentos) y ¿A qué precio ofertar?

Claude Code, Cursor, VS Code y clientes que aceptan cabeceras (recomendado)

La llave va en una cabecera y no queda en la dirección. Claude Code:

Terminal
claude mcp add --transport http licitia https://bqjhmvnlvdpvbxvqtodh.supabase.co/functions/v1/mcp --header "Authorization: Bearer lic_…"

Cursor (~/.cursor/mcp.json) o VS Code (.vscode/mcp.json, con "servers" y "type": "http"):

JSON
{
  "mcpServers": {
    "licitia": {
      "url": "https://bqjhmvnlvdpvbxvqtodh.supabase.co/functions/v1/mcp",
      "headers": { "Authorization": "Bearer lic_…" }
    }
  }
}

Claude (web y escritorio), ChatGPT y clientes que solo piden una dirección

En Claude: Configuración → Conectores → Agregar conector personalizado. Pega la dirección con tu llave al final:

Dirección con llave
https://bqjhmvnlvdpvbxvqtodh.supabase.co/functions/v1/mcp?apikey=lic_…

Funciona, pero la llave queda guardada dentro de la dirección. Usa una llave solo para el asistente, con un nombre que la identifique, y revócala en Integraciones si dejas de usarla.

Está en los planes Agencia y Corporativo, con los mismos límites por minuto de la API.

Webhooks

Configura en Herramientas → Integraciones una URL https y elige qué eventos recibir. Cada evento llega como un POST.

EventoCuándo llegaExtra en datos
proceso.nuevoSe publica un proceso que coincide con tu radar.—
proceso.adendaSale una adenda en un proceso de tu kanban.cambio.adendas_antes, adendas_ahora, ultima_adenda_fecha
proceso.cierre_cambiadoCambia la fecha de cierre de un proceso de tu kanban.cambio.cierre_antes, cierre_ahora
proceso.estadoCambia el estado en SECOP de un proceso de tu kanban (evaluación, adjudicado, desierto, cancelado…).cambio.estado_antes, estado_ahora
proceso.adjudicadoSe adjudica un proceso de tu kanban.cambio.proveedor, nit, valor
competidor.ganoUno de los 30 rivales más frecuentes de tu red de competidores gana un proceso.competidor.nombre, nit, coincidieron; y cambio con la adjudicación

«Tu kanban» son los procesos que tienes como tarjeta, sin archivar. «Tu red» es la que calculaste en Mi Panel → Mis Adjudicaciones. Ejemplo de proceso.nuevo:

Cuerpo del POST
{
  "id": "0b8e5c1e-6f61-4c3e-9a77-3b6e3c2b9f10",
  "tipo": "proceso.nuevo",
  "creado": "2026-10-30T14:20:22.009Z",
  "version": "2026-10-30",
  "datos": {
    "codigo_proceso": "CO1.REQ.10598757",
    "objeto": "Interventoría técnica, administrativa y financiera…",
    "entidad": "GOBERNACION DEL DEPARTAMENTO DEL CESAR",
    "departamento": "Cesar", "municipio": "Valledupar",
    "valor": 3100000000, "modalidad": "Concurso de méritos abierto",
    "tipo_contrato": "Interventoría", "categorias": ["consultoría"],
    "fecha_publicacion": "2026-10-30", "fecha_cierre": "2026-11-14",
    "fuente": "SECOP II", "link": "https://community.secop.gov.co/…"
  }
}

Cabeceras

  • LicitIA-Evento: proceso.nuevo o prueba.
  • LicitIA-Entrega: id único del envío; si te llega dos veces, descarta el repetido.
  • LicitIA-Firma: t=<unix>,v1=<firma>.

Verificar la firma

Calcula HMAC-SHA256 con tu secreto (whsec_…) sobre <t>.<cuerpo crudo>, compáralo con v1 y rechaza si t tiene más de 5 minutos. Usa el cuerpo tal como llegó, antes de convertirlo a JSON.

Node.js (Express)
const crypto = require("crypto");

app.post("/licitia", express.raw({ type: "application/json" }), (req, res) => {
  const firma = req.get("LicitIA-Firma") || "";
  const t = Number((firma.match(/t=(\d+)/) || [])[1]);
  const v1 = (firma.match(/v1=([0-9a-f]+)/) || [])[1] || "";
  const esperada = crypto.createHmac("sha256", process.env.LICITIA_SECRETO)
    .update(`${t}.${req.body.toString("utf8")}`).digest("hex");
  const vigente = Math.abs(Date.now() / 1000 - t) <= 300;
  if (!vigente || v1.length !== esperada.length ||
      !crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(esperada))) {
    return res.sendStatus(401);
  }
  const evento = JSON.parse(req.body);
  // … procesa evento.datos
  res.sendStatus(200);
});
Python (Flask)
import hmac, hashlib, os, re, time
from flask import request, abort

@app.post("/licitia")
def licitia():
    firma = request.headers.get("LicitIA-Firma", "")
    t = int((re.search(r"t=(\d+)", firma) or [0, 0])[1])
    v1 = (re.search(r"v1=([0-9a-f]+)", firma) or [0, ""])[1]
    cuerpo = request.get_data(as_text=True)
    esperada = hmac.new(os.environ["LICITIA_SECRETO"].encode(),
                        f"{t}.{cuerpo}".encode(), hashlib.sha256).hexdigest()
    if abs(time.time() - t) > 300 or not hmac.compare_digest(v1, esperada):
        abort(401)
    evento = request.get_json()
    # … procesa evento["datos"]
    return "", 200
PHP
<?php
$cuerpo = file_get_contents('php://input');
$firma  = $_SERVER['HTTP_LICITIA_FIRMA'] ?? '';
preg_match('/t=(\d+)/', $firma, $mt);
preg_match('/v1=([0-9a-f]+)/', $firma, $mv);
$t = (int)($mt[1] ?? 0);
$esperada = hash_hmac('sha256', $t . '.' . $cuerpo, getenv('LICITIA_SECRETO'));
if (abs(time() - $t) > 300 || !hash_equals($esperada, $mv[1] ?? '')) {
    http_response_code(401); exit;
}
$evento = json_decode($cuerpo, true);
// … procesa $evento['datos']
http_response_code(200);

Entregas y reintentos

  • Responde 2xx en menos de 10 segundos; procesa lo pesado después.
  • Ante un error de red, 429 o 5xx reintentamos dos veces (a 1 y 4 segundos). Un 4xx no se reintenta.
  • Tras 20 envíos fallidos seguidos el webhook se pausa; se reactiva al guardarlo de nuevo.
  • En Integraciones ves los últimos envíos y el código con que respondió tu servidor, y puedes mandar un evento de prueba.

Zapier y Make

  • Zapier: crea un Zap con el disparador Webhooks by Zapier → Catch Hook, copia su URL en Integraciones y pulsa Enviar prueba para que Zapier vea los campos.
  • Make: agrega el módulo Webhooks → Custom webhook, copia la URL y envía la prueba para que Make determine la estructura.
  • Ideas: cada proceso nuevo como tarjeta en Trello o Asana, fila en Google Sheets o mensaje en Teams o Slack.

Zapier y Make no verifican la firma por sí mismos. Si el flujo hace algo sensible, verifícala con un paso de código antes de seguir.

Datos y licencia

  • Fuente: Agencia Nacional de Contratación Pública – Colombia Compra Eficiente (SECOP I y II, datos.gov.co), bajo licencia CC BY-SA 4.0. Si publicas datos obtenidos por la API, cita la fuente.
  • De personas naturales solo se entrega el nombre, nunca el documento. Quien pidió que se suprima su nombre aparece como «Persona natural». La API no consulta cédulas.
  • Son registros públicos de SECOP: no son una evaluación de las empresas. Una corrección se pide ante la entidad que publicó el dato.
  • Versionado: /v1 no tendrá cambios que rompan tus integraciones. Lo nuevo se agrega como campos o recursos adicionales; un cambio incompatible saldría como /v2, avisado con anticipación.

¿Dudas o algo no funciona? Escribe a info@licitia.com.co con el request_id de la respuesta.