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
- En CumpleOL entre a Configuración → API e integraciones (requiere el permiso de administrar la API).
- Active el interruptor API habilitada y cree una llave con los alcances que necesita su integración.
- 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.
- 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).
| Alcance | Permite |
|---|---|
proveedores:leer | Consultar proveedores y contratos |
proveedores:escribir | Dar de alta, editar e invitar proveedores |
documentos:leer | Consultar obligaciones y documentos (incluye descarga) |
documentos:escribir | Cargar documentos a las obligaciones |
trabajadores:leer | Consultar trabajadores asignados |
trabajadores:escribir | Registrar trabajadores asignados |
cumplimiento:leer | Consultar 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 ruta | Alcance | Descripción |
|---|---|---|
GET /proveedores | proveedores:leer | Lista paginada. Filtros: rfc, semaforo, estado. |
GET /proveedores/{id} | proveedores:leer | Detalle del proveedor. |
POST /proveedores | proveedores:escribir | Alta (rfc y razonSocial obligatorios). Genera sus obligaciones. |
PATCH /proveedores/{id} | proveedores:escribir | Edita solo los campos enviados; acepta estado (Activo, Bloqueado, Baja…). |
POST /proveedores/{id}/invitar | proveedores:escribir | Envía o reenvía la invitación al portal del proveedor. |
GET /proveedores/{id}/cumplimiento | cumplimiento:leer | Puntaje, semáforo y motivos calculados al momento. |
GET /proveedores/{id}/obligaciones | documentos:leer | Obligaciones documentales. Filtros: estado, periodo. |
GET /proveedores/{id}/trabajadores | trabajadores:leer | Trabajadores asignados. Filtro: activo. |
POST /proveedores/{id}/trabajadores | trabajadores:escribir | Registra un trabajador (nombre, rfc, curp, nss, puesto, contratoId, fechaAsignacion). |
POST /obligaciones/{id}/documentos | documentos:escribir | Carga un documento (multipart, campo «archivo»). |
GET /documentos/{id} | documentos:leer | Metadatos, estado de revisión y validaciones automáticas. |
GET /documentos/{id}/archivo | documentos:leer | Descarga el archivo original. |
GET /contratos | proveedores:leer | Contratos. Filtros: proveedorId, estado. |
GET /cumplimiento/resumen | cumplimiento:leer | Proveedores 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»." } }
| HTTP | Códigos | Cuándo |
|---|---|---|
| 401 | sin_llave, llave_invalida, llave_revocada, llave_expirada | Falta la llave o no es válida. |
| 403 | alcance_insuficiente, ip_no_permitida, api_deshabilitada, plan_sin_api, cuenta_suspendida | La llave es válida pero no tiene permiso. |
| 404 | no_encontrado, ruta_no_encontrada | El recurso no existe o pertenece a otra cuenta. |
| 409 | duplicado | Ya existe un proveedor con ese RFC o un trabajador repetido. |
| 413 / 415 | archivo_grande, tipo_no_soportado | Archivo demasiado grande o tipo de contenido incorrecto. |
| 422 | datos_invalidos | Validación (RFC, CURP, NSS, filtros, JSON mal formado, documento rechazado por formato). |
| 429 | limite_excedido | Superó el límite de solicitudes; respete Retry-After. |
| 500 | error_interno | Error inesperado; reintente con espera. |
Límites
- 300 solicitudes por minuto por llave (ventana deslizante). Al excederlo responderemos
429conRetry-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}}
| Evento | Cuándo |
|---|---|
documento.cargado | Un proveedor (o la API) cargó un documento que queda en revisión. |
documento.aprobado | Un documento fue aprobado (manual o automáticamente). |
documento.rechazado | Un documento fue rechazado; incluye el motivo. |
obligacion.vencida | Una obligación llegó a su fecha límite sin documento. |
proveedor.semaforo | Cambió el semáforo de cumplimiento de un proveedor. |
proveedor.creado | Se dio de alta un proveedor. |
proveedor.lista_sat | Un proveedor aparece en listas del SAT (69-B / 69). |
ping | Evento 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:
- Lea el cuerpo crudo (sin volver a serializar el JSON).
- Calcule el HMAC-SHA256 de
t.cuerpocon su secreto y compárelo conv1en tiempo constante. - Rechace marcas de tiempo con más de 5 minutos de diferencia (evita reenvíos).
- Use
iddel cuerpo o el encabezadoX-CumpleOL-Entregapara descartar duplicados.
Entregas y reintentos
- Responda con un código
2xxen menos de 10 segundos; procese en segundo plano si tarda más. - No seguimos redirecciones: un
3xxcuenta 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).