Saltar al contenido

Desarrolladores

API REST v1 y webhooks

Integre el control de cumplimiento REPSE con su ERP, su sistema de compras o su mesa de ayuda: dé de alta proveedores, cargue documentos, consulte el semáforo antes de liberar un pago y reciba avisos en tiempo real.

Descargar especificación OpenAPI (openapi.json)

Disponible en los planes Profesional, Corporativo y Enterprise. La API es una herramienta de apoyo para su proceso; no constituye asesoría legal.

Inicio rápido

  1. En CumpleOL entre a Configuración → API e integraciones (requiere el permiso de administrar la API).
  2. Active el interruptor API habilitada y cree una llave con los alcances que necesita su integración.
  3. Copie la llave: se muestra una sola vez. Guárdela en el almacén de secretos de su sistema, nunca en el código fuente.
  4. Haga su primera llamada:
curl https://repse.olsistemas.com.mx/api/v1/cumplimiento/resumen \
  -H "Authorization: Bearer $CUMPLEOL_LLAVE"

Autenticación

Cada solicitud debe llevar la llave en el encabezado Authorization con el esquema Bearer (o, si su herramienta no lo permite, en X-Api-Key). Las llaves tienen el formato co_live_ seguido de 32 caracteres.

Authorization: Bearer co_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  • Guardamos solo la huella SHA-256 de la llave y un prefijo visible (por ejemplo co_live_a1b2) para que la identifique.
  • Puede limitar cada llave a una lista de IPs o rangos CIDR (IPv4 e IPv6) y ponerle fecha de expiración.
  • Una llave deja de funcionar si se revoca, si expira, si se desactiva la API de la cuenta, si el plan no incluye API o si la cuenta está suspendida.
  • Toda escritura queda en la bitácora inalterable de la cuenta con el usuario api:<prefijo>.
  • Use siempre HTTPS. Las respuestas llevan Cache-Control: no-store.

Alcances

Asigne a cada llave solo los alcances que necesite (principio de mínimo privilegio).

AlcancePermite
proveedores:leerConsultar proveedores y contratos
proveedores:escribirDar de alta, editar e invitar proveedores
documentos:leerConsultar obligaciones y documentos (incluye descarga)
documentos:escribirCargar documentos a las obligaciones
trabajadores:leerConsultar trabajadores asignados
trabajadores:escribirRegistrar trabajadores asignados
cumplimiento:leerConsultar puntaje, semáforo y resumen de cumplimiento

Endpoints

Base: https://repse.olsistemas.com.mx/api/v1. Todo se limita a la cuenta dueña de la llave: un identificador de otra cuenta responde 404. JSON en camelCase; fechas AAAA-MM-DD y marcas de tiempo UTC en ISO 8601.

Método y rutaAlcanceDescripción
GET /proveedoresproveedores:leerLista paginada. Filtros: rfc, semaforo, estado.
GET /proveedores/{id}proveedores:leerDetalle del proveedor.
POST /proveedoresproveedores:escribirAlta (rfc y razonSocial obligatorios). Genera sus obligaciones.
PATCH /proveedores/{id}proveedores:escribirEdita solo los campos enviados; acepta estado (Activo, Bloqueado, Baja…).
POST /proveedores/{id}/invitarproveedores:escribirEnvía o reenvía la invitación al portal del proveedor.
GET /proveedores/{id}/cumplimientocumplimiento:leerPuntaje, semáforo y motivos calculados al momento.
GET /proveedores/{id}/obligacionesdocumentos:leerObligaciones documentales. Filtros: estado, periodo.
GET /proveedores/{id}/trabajadorestrabajadores:leerTrabajadores asignados. Filtro: activo.
POST /proveedores/{id}/trabajadorestrabajadores:escribirRegistra un trabajador (nombre, rfc, curp, nss, puesto, contratoId, fechaAsignacion).
POST /obligaciones/{id}/documentosdocumentos:escribirCarga un documento (multipart, campo «archivo»).
GET /documentos/{id}documentos:leerMetadatos, estado de revisión y validaciones automáticas.
GET /documentos/{id}/archivodocumentos:leerDescarga el archivo original.
GET /contratosproveedores:leerContratos. Filtros: proveedorId, estado.
GET /cumplimiento/resumencumplimiento:leerProveedores por semáforo, documentos en revisión y obligaciones vencidas.
GET /eventos(cualquiera)Tipos de evento disponibles para webhooks.

Valores de semaforo: SinDatos, Verde, Amarillo, Rojo. Estados de proveedor: Invitado, EnAlta, Activo, Bloqueado, Baja. Estados de obligación: Pendiente, EnRevision, Aprobada, Rechazada, Vencida, NoAplica. Periodos: 2026-10 (mensual), 2026-B5 (bimestre), 2026-C2 (cuatrimestre), 2026 (anual), UNICA.

Alta de proveedor

curl -X POST https://repse.olsistemas.com.mx/api/v1/proveedores \
  -H "Authorization: Bearer $CUMPLEOL_LLAVE" \
  -H "Content-Type: application/json" \
  -d '{"rfc":"LIM010101AB1","razonSocial":"Limpieza Industrial del Norte SA de CV",
       "registroRepse":"AR12345/2024","repseVigenteHasta":"2027-06-30",
       "contactoNombre":"Laura Pérez","contactoCorreo":"laura@limpiezanorte.mx"}'

Cargar un documento

Multipart con el campo archivo (PDF, XML, ZIP de XML, JPG o PNG). El documento pasa por la misma validación automática que en el portal y la respuesta incluye el resultado en validaciones.

curl -X POST https://repse.olsistemas.com.mx/api/v1/obligaciones/1234/documentos \
  -H "Authorization: Bearer $CUMPLEOL_LLAVE" \
  -F "archivo=@opinion-imss-2026-10.pdf"

Consultar el semáforo antes de pagar

curl https://repse.olsistemas.com.mx/api/v1/proveedores/57/cumplimiento \
  -H "Authorization: Bearer $CUMPLEOL_LLAVE"
{
  "proveedorId": 57,
  "rfc": "LIM010101AB1",
  "puntaje": 88,
  "semaforo": "Amarillo",
  "exigibles": 25, "aprobadas": 21, "enRevision": 2, "pendientes": 4, "vencidas": 2, "rechazadas": 0,
  "motivos": ["Cumplimiento de 88 %", "2 documento(s) esperan revisión"],
  "calculadoAl": "2026-11-13"
}

Paginación

Las listas aceptan ?pagina=1&porPagina=50 (máximo 200 por página) y responden con un envoltorio:

{
  "datos": [ { "id": 57, "rfc": "LIM010101AB1", "razonSocial": "...", "semaforo": "Verde", "puntaje": 100 } ],
  "pagina": 1,
  "porPagina": 50,
  "total": 134
}

También enviamos los encabezados X-Total-Count, X-Pagina y X-Por-Pagina.

Errores

Todos los errores tienen la misma forma:

HTTP/1.1 403 Forbidden
{ "error": { "codigo": "alcance_insuficiente", "mensaje": "La llave no tiene el alcance «documentos:escribir»." } }
HTTPCódigosCuándo
401sin_llave, llave_invalida, llave_revocada, llave_expiradaFalta la llave o no es válida.
403alcance_insuficiente, ip_no_permitida, api_deshabilitada, plan_sin_api, cuenta_suspendidaLa llave es válida pero no tiene permiso.
404no_encontrado, ruta_no_encontradaEl recurso no existe o pertenece a otra cuenta.
409duplicadoYa existe un proveedor con ese RFC o un trabajador repetido.
413 / 415archivo_grande, tipo_no_soportadoArchivo demasiado grande o tipo de contenido incorrecto.
422datos_invalidosValidación (RFC, CURP, NSS, filtros, JSON mal formado, documento rechazado por formato).
429limite_excedidoSuperó el límite de solicitudes; respete Retry-After.
500error_internoError inesperado; reintente con espera.

Límites

  • 300 solicitudes por minuto por llave (ventana deslizante). Al excederlo responderemos 429 con Retry-After: 60.
  • Máximo 200 elementos por página.
  • Archivos de hasta el tamaño máximo configurado para su cuenta (el mismo que en el portal).
  • Hasta 25 llaves activas y 10 webhooks por cuenta.

Webhooks

Registre la URL https de su sistema en API e integraciones y elija los eventos. Al ocurrir un evento enviamos un POST JSON:

POST /webhooks/cumpleol HTTP/1.1
Content-Type: application/json; charset=utf-8
User-Agent: CumpleOL-Webhooks/1.0
X-CumpleOL-Evento: documento.cargado
X-CumpleOL-Entrega: 9812
X-CumpleOL-Firma: t=1794585600,v1=5f2b8c...e41a

{"id":"3f9c...","evento":"documento.cargado","creado":"2026-11-13T18:00:00Z",
 "datos":{"documentoId":4410,"obligacionId":1234,"proveedorId":57,"rfc":"LIM010101AB1","tipo":"IMSS_OPINION","periodo":"2026-10","validacion":100}}
EventoCuándo
documento.cargadoUn proveedor (o la API) cargó un documento que queda en revisión.
documento.aprobadoUn documento fue aprobado (manual o automáticamente).
documento.rechazadoUn documento fue rechazado; incluye el motivo.
obligacion.vencidaUna obligación llegó a su fecha límite sin documento.
proveedor.semaforoCambió el semáforo de cumplimiento de un proveedor.
proveedor.creadoSe dio de alta un proveedor.
proveedor.lista_satUn proveedor aparece en listas del SAT (69-B / 69).
pingEvento de prueba enviado desde Integraciones.

Verificación de la firma

Cada envío incluye X-CumpleOL-Firma: t=<unix>,v1=<firma>, donde la firma es el HMAC-SHA256 en hexadecimal del texto t + "." + cuerpo con el secreto del webhook (se muestra una sola vez al crearlo). Para verificarla:

  1. Lea el cuerpo crudo (sin volver a serializar el JSON).
  2. Calcule el HMAC-SHA256 de t.cuerpo con su secreto y compárelo con v1 en tiempo constante.
  3. Rechace marcas de tiempo con más de 5 minutos de diferencia (evita reenvíos).
  4. Use id del cuerpo o el encabezado X-CumpleOL-Entrega para descartar duplicados.

Entregas y reintentos

  • Responda con un código 2xx en menos de 10 segundos; procese en segundo plano si tarda más.
  • No seguimos redirecciones: un 3xx cuenta como fallo.
  • Reintentamos hasta 8 veces con espera exponencial (30 s, 1, 2, 4, 8, 16 y 32 min). Después la entrega queda como fallida y puede reintentarla desde el historial.
  • Tras 20 fallos seguidos el webhook se desactiva y avisamos por correo a los administradores de la cuenta.
  • Por seguridad solo entregamos a direcciones públicas: se bloquean IPs privadas, de loopback, link-local y de metadatos de nube.

Ejemplos

C# (.NET)

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;

// 1) Consultar la API
var http = new HttpClient { BaseAddress = new Uri("https://repse.olsistemas.com.mx/api/v1/") };
http.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("CUMPLEOL_LLAVE"));
var pagina = await http.GetFromJsonAsync<JsonElement>("proveedores?semaforo=Rojo&porPagina=200");
foreach (var p in pagina.GetProperty("datos").EnumerateArray())
    Console.WriteLine($"{p.GetProperty("rfc")} {p.GetProperty("puntaje")}");

// 2) Verificar la firma de un webhook (ASP.NET Core minimal API)
app.MapPost("/webhooks/cumpleol", async (HttpRequest req) =>
{
    using var lector = new StreamReader(req.Body, Encoding.UTF8);
    var cuerpo = await lector.ReadToEndAsync();
    var partes = req.Headers["X-CumpleOL-Firma"].ToString().Split(',')
        .Select(x => x.Split('=', 2)).Where(x => x.Length == 2).ToDictionary(x => x[0], x => x[1]);
    if (!partes.TryGetValue("t", out var t) || !partes.TryGetValue("v1", out var v1)) return Results.Unauthorized();
    if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - long.Parse(t)) > 300) return Results.Unauthorized();
    var secreto = Environment.GetEnvironmentVariable("CUMPLEOL_WEBHOOK_SECRETO")!;
    var esperada = Convert.ToHexStringLower(
        HMACSHA256.HashData(Encoding.UTF8.GetBytes(secreto), Encoding.UTF8.GetBytes($"{t}.{cuerpo}")));
    if (!CryptographicOperations.FixedTimeEquals(Encoding.ASCII.GetBytes(esperada), Encoding.ASCII.GetBytes(v1)))
        return Results.Unauthorized();
    // ... encolar el procesamiento y responder rápido
    return Results.Ok();
});

Python

import hashlib, hmac, os, time
import requests

# 1) Consultar la API
llave = os.environ["CUMPLEOL_LLAVE"]
r = requests.get("https://repse.olsistemas.com.mx/api/v1/proveedores",
                 params={"semaforo": "Rojo", "porPagina": 200},
                 headers={"Authorization": f"Bearer {llave}"}, timeout=30)
r.raise_for_status()
for p in r.json()["datos"]:
    print(p["rfc"], p["puntaje"])

# 2) Cargar un documento
with open("opinion-imss-2026-10.pdf", "rb") as f:
    r = requests.post("https://repse.olsistemas.com.mx/api/v1/obligaciones/1234/documentos",
                      headers={"Authorization": f"Bearer {llave}"}, files={"archivo": f}, timeout=60)
print(r.status_code, r.json())

# 3) Verificar la firma de un webhook (por ejemplo en Flask)
def firma_valida(cuerpo: bytes, encabezado: str, secreto: str, tolerancia: int = 300) -> bool:
    partes = dict(p.split("=", 1) for p in encabezado.split(",") if "=" in p)
    t, v1 = partes.get("t"), partes.get("v1")
    if not t or not v1 or abs(time.time() - int(t)) > tolerancia:
        return False
    esperada = hmac.new(secreto.encode(), f"{t}.".encode() + cuerpo, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperada, v1)

¿Dudas sobre la integración? Contáctenos. La especificación completa está en /api/v1/openapi.json (puede importarla en Postman, Insomnia o generar un cliente).