Licia.
Licia · Análisis de un pliego
Ficha de una licitación en Licia con el veredicto Go/No-Go y las citas del pliego.

Un pliego real, leído por Licia

Español Català Euskara Galego English
Iniciar sesión Solicitar acceso

API v1.0.0 y webhooks

API de solo lectura sobre el estado de tu empresa en Licia: las licitaciones que te encajan, el análisis de cada pliego con sus citas, tu embudo y tus avisos. Es la misma información que ves en la aplicación, leída con las mismas consultas.

Solo lectura, a propósito

La API devuelve lo que ya ves en la aplicación, leído con las mismas consultas: no hay un modelo de datos paralelo que se pueda desfasar. Tampoco hay endpoints de escritura, y no es una carencia temporal: nada en Licia presenta documentación en tu nombre ante un organismo, y una API de escritura convertiría nuestras tablas internas en el contrato de integración de otro. Una petición POST, PUT o DELETE responde 405 diciendo justo esto.

Todo el contenido generado por IA requiere revisión humana antes de presentarse. Los veredictos y los borradores que devuelve esta API son apoyo a una decisión, no la decisión.

Autenticación

Un token por empresa, creado desde Perfil de empresa → API. Se muestra una única vez al crearlo: guardamos solo su hash, así que si se pierde no se puede recuperar —se crea otro y se revoca el anterior—. Empieza por licia_ y viaja en la cabecera Authorization:

curl -H "Authorization: Bearer licia_…" \
     https://licia.es/api/v1/licitaciones

El token autoriza exactamente una empresa: la suya. Quien puede crearlo o revocarlo es quien puede configurar esa empresa, la misma regla que gobierna el resto de /perfil. Revocar surte efecto en la petición siguiente.

Un token lee lo mismo que lee la cuenta que lo creó, y solo mientras esa cuenta siga teniendo acceso a la empresa: si a esa persona se le retira el sitio en el equipo, el token deja de funcionar con ella (401 acceso_revocado). Para una integración que tiene que sobrevivir a la salida de alguien, créala desde la cuenta propietaria.

Trátalo como una contraseña: cualquiera que lo tenga puede leer tus licitaciones, tus veredictos y tu embudo. No lo pongas en una URL ni en un repositorio.

Límite de peticiones

60 peticiones por minuto y por token. Al pasarlo, la respuesta es 429 con la cabecera Retry-After en segundos. Las páginas admiten hasta 100 resultados (limite) y se recorren con pagina; cada respuesta trae total y paginas.

Errores

Siempre JSON, con la misma forma:

{"error": {"codigo": "token_invalido", "mensaje": "Ese token no es válido o ha sido revocado."}}

401 token ausente, desconocido o revocado —los tres con el mismo mensaje, para no confirmar si una cadena filtrada fue alguna vez válida—, más acceso_revocado, que sí se distingue porque para verlo hay que tener ya un token válido; 400 parámetro no reconocido; 404 recurso inexistente; 405 escritura; 429 límite de peticiones.

Fechas y horas

Todas en ISO 8601 y todas con zona: nunca hay que suponerla. Hay dos tipos, y llevan zonas distintas a propósito.

plazo es el cierre del procedimiento, tal y como lo publica el órgano de contratación: una hora de pared española, en Europe/Madrid (+01:00 o +02:00 según la fecha). Es la misma hora que se ve en la ficha de la licitación y la misma que viaja en el calendario .ics. La PLACSP publica estas horas sin zona, así que la fijamos nosotros al ingerir y no al servirla —convertirla dos veces es exactamente como un plazo se mueve una hora—.

El resto (publicado, veredicto_fecha, creado, enviado…) son instantes nuestros y van en UTC.

{"plazo": "2026-09-14T14:00:00+02:00", "publicado": "2026-09-01T07:12:44+00:00"}

Endpoints

Esta lista se genera del esquema OpenAPI de la propia aplicación: si un endpoint existe, está aquí.

GET /api/v1/avisos

Los avisos de la empresa

La misma bandeja que /avisos, y por tanto exactamente los eventos que el webhook envía: es lo que permite recuperar lo ocurrido mientras un endpoint estaba caído, sin que la aplicación y la integración puedan contar historias distintas.

  • limite — Cuántos avisos devolver (máximo 100).

GET /api/v1/crm/deals

Oportunidades sincronizadas en CRM

Lista de oportunidades/deals creados y sincronizados en HubSpot, Pipedrive o Salesforce para las licitaciones de esta empresa.

GET /api/v1/embudo

El embudo de la empresa

El tablero del panel: cuántas licitaciones hay en cada fase y cuáles son, con la decisión del equipo y su fecha. Las fases y sus nombres son los mismos que usa la aplicación.

GET /api/v1/licitaciones

Licitaciones de la empresa

El listado que ve la empresa, con su veredicto, su fase del embudo y su borrador si lo hay. Sin filtros devuelve la convocatoria abierta de los CPV vigilados (lo que muestra /licitaciones). Con `analizadas=1` devuelve las que ya tienen veredicto, sin restringir por CPV, y con `seguidas=1` las que están en el embudo: entre los tres, el panel se reconstruye entero.

  • estado — `abiertas` (por defecto) o `todas`, que incluye las cerradas.
  • analizadas — `1` para devolver solo las que tienen veredicto.
  • seguidas — `1` para devolver solo las del embudo.
  • veredicto — Filtra por `go`, `no_go` o `needs_human_check`.
  • etapa — Filtra por fase del embudo.
  • cpv — Prefijo CPV. Varios, separados por comas (`45,72`): una licitación vale si encaja con cualquiera de ellos.
  • busqueda — Texto en título, resumen o expediente.
  • pagina — Página, desde 1.
  • limite — Resultados por página (máximo 100).

GET /api/v1/licitaciones/{tender_id}

Análisis de una licitación

La ficha completa para esta empresa: la licitación, los requisitos extraídos del pliego con sus citas textuales, el veredicto Go/No-Go con su razonamiento y la fase del embudo. Las citas son el texto congelado en el momento de la extracción, con el identificador del fragmento: un veredicto sin su cita es una opinión.

  • tender_id (obligatorio)

GET /api/v1/perfil

La empresa del token

Identifica la empresa a la que pertenece el token y devuelve los contadores del panel: licitaciones indexadas, novedades de los últimos 7 días en los CPV vigilados, veredictos GO vigentes, borradores pendientes y total del embudo.

Reconstruir el panel

El panel de la aplicación no usa nada que no esté aquí. Con tres llamadas se rehace entero:

  • /api/v1/perfil — la tira de contadores: licitaciones indexadas, novedades de la semana en tus CPV, veredictos GO vigentes, borradores pendientes y total del embudo.
  • /api/v1/licitaciones?analizadas=1 — la lista de trabajo. Cada fila trae su veredicto y su borrador, que es lo que la agrupa en «revisar», «genera el borrador» y «borrador generado».
  • /api/v1/licitaciones sin filtros — el radar: la convocatoria abierta de los CPV que vigilas. Las que llegan con veredicto y etapa a null son las que nadie ha mirado todavía.
  • /api/v1/embudo — el tablero por fases, con la decisión del equipo y desde cuándo está en esa fase.

Lo que no viaja: el estado de ninguna otra empresa. Ni siquiera el aviso anónimo de «este lote ya está reservado» que la aplicación muestra en pantalla —una respuesta JSON se reenvía como se reenvía un fichero, y una pantalla no—. Solo tus propias ofertas.

Webhooks firmados

Un endpoint HTTPS por empresa, configurado en Perfil de empresa → API. Recibe los mismos avisos que el correo y los canales de chat, porque son las mismas filas: si un aviso está en «Solo en la app», tampoco se envía por webhook. Los eventos salen del servidor de análisis, no de la web.

Tipos de evento:

  • new_match — Licitaciones nuevas que encajan
  • draft_ready — Borradores que escribe el autopiloto
  • deadline — Plazos que se acercan
  • expiry — Documentos que caducan
  • outcome — Resultados publicados
  • job_done — Análisis terminados
  • saved_search — Coincidencias de tus búsquedas guardadas
  • go_verdict — Veredicto GO comercial
  • stage_changed — Cambio de fase en el embudo

Cuerpo de la petición (POST, JSON):

{
  "id": 1843,
  "tipo": "deadline",
  "creado": "2026-08-31T09:00:00+00:00",
  "empresa": {"id": 7, "nombre": "ACME Servicios SL"},
  "titulo": "Quedan 3 días: Servicios de mantenimiento",
  "cuerpo": "Plazo de presentación: 03/09/2026 (3 días).",
  "enlace": "https://licia.es/licitaciones/912",
  "licitacion": {"id": 912, "expediente": "2026/PA/12", "titulo": "…",
                 "api": "https://licia.es/api/v1/licitaciones/912"}
}

Cabeceras:

  • X-Licia-Event — el tipo de evento.
  • X-Licia-Delivery — el identificador del aviso; el mismo en cada reintento, para que puedas descartar duplicados.
  • X-Licia-Attempt — número de intento.
  • X-Licia-Timestamp — el instante de la firma, en segundos Unix.
  • X-Licia-Signaturet=<timestamp>,v1=<hmac>.

Para verificar: calcula HMAC-SHA256(secreto, "<timestamp>." + cuerpo_tal_cual) en hexadecimal y compáralo con v1 usando una comparación en tiempo constante. Comprueba también que el timestamp es reciente (unos cinco minutos): va dentro de lo firmado precisamente para que una copia de un evento antiguo no se pueda reenviar como nueva. Firma sobre el cuerpo en bruto, antes de parsear el JSON: cualquier reserialización cambia los bytes y por tanto la firma.

import hashlib, hmac, time

def valido(secreto, cabecera, cuerpo_bytes, tolerancia=300):
    partes = dict(p.split("=", 1) for p in cabecera.split(",") if "=" in p)
    t, firma = int(partes["t"]), partes["v1"]
    if abs(time.time() - t) > tolerancia:
        return False
    esperada = hmac.new(secreto.encode(),
                        f"{t}.".encode() + cuerpo_bytes,
                        hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperada, firma)

El secreto se muestra una sola vez, al conectar el webhook o al rotarlo, y es un credencial: no lo registres en logs. Rotarlo invalida las firmas posteriores, no las ya entregadas.

Reintentos y fallos. Una respuesta 2xx cierra la entrega. Un fallo temporal (tiempo de espera, 5xx, 429) se reintenta hasta 3 veces con espera creciente. Un rechazo definitivo (400, 401, 403, 404, 410) falla al primer intento y lo verás en el aviso, en /avisos, junto al resto de canales: cada canal lleva su propia cuenta, así que un endpoint caído nunca te cuesta el correo del mismo aviso.

Qué no hacemos. No seguimos redirecciones —configura la URL final—, no aceptamos http://, no enviamos a direcciones que no sean públicas de internet (ni por nombre ni por redirección de DNS) y leemos como mucho unos pocos kilobytes de tu respuesta. Basta con que devuelvas 200 y un cuerpo vacío.

Si tu endpoint estuvo caído. /api/v1/avisos devuelve la misma bandeja que la aplicación —las mismas filas que el webhook envía—, así que se recupera lo ocurrido sin que la integración y la pantalla puedan contar historias distintas.

Conectores CRM (HubSpot, Pipedrive, Salesforce)

Licia sincroniza automáticamente tus oportunidades comerciales con tu CRM sin necesidad de desarrollo adicional:

  • Veredicto GO: cuando una licitación encaja y recibe un veredicto viable (go_verdict), se crea automáticamente la oportunidad (Deal / Opportunity) en tu CRM con el título del expediente, importe estimado, fecha límite de presentación y enlace a Licia.
  • Cambios de fase: cuando mueves la licitación en el embudo (de evaluación a preparación, presentada, adjudicada o descartada), el conector actualiza la etapa del deal en tu CRM.
  • Seguridad y asientos: la integración comprueba en cada entrega que el usuario que la configuró mantiene su asiento en la empresa (_creator_has_access).

Puedes conectar tu CRM directamente desde Perfil → API y CRM. Las oportunidades creadas se pueden consultar mediante la API en /api/v1/crm/deals.

Superficie Zapier y Make

Para conectar Licia con cualquier otra herramienta (Notion, Airtable, Monday, Asana, Google Sheets o tu propio CRM), utiliza la combinación de Webhooks salientes y la API REST v1:

  1. Disparador (Trigger): Crea un webhook en Zapier («Webhooks by Zapier → Catch Hook») o en Make («Custom Webhook») y copia su URL HTTPS en el apartado Webhook de eventos de tu perfil.
  2. Filtro de evento: Filtra por la propiedad tipo:
    • go_verdict: para crear oportunidades cuando un pliego es viable.
    • stage_changed: para sincronizar cambios de fase en tu gestor de proyectos.
    • deadline: para enviar alertas urgentes a canales específicos.
  3. Acciones complementarias (Actions): Utiliza una petición HTTP con cabecera Authorization: Bearer <tu_token_api> hacia /api/v1/licitaciones/{id} para consultar el desglose de requisitos, criterios de adjudicación o resumen ejecutivo.