API v1 · Guía del integrador · 1.3.0

Integrar Valida por API

Esta guía es todo lo que necesita un desarrollador para integrarse sin hablar con nadie: cómo autenticarse, cómo reintentar sin cobrar dos veces, qué versión de lista consultó, cómo se mide el paquete, cuándo se bloquea el servicio (spoiler: nunca por consumo) y cómo recibir alertas. Cada endpoint trae ejemplos en curl, JavaScript y Python.

Base URL: https://api.valida.metrikone.co · Vigente desde: 9 de septiembre de 2026

Contenido de la guía16 secciones

1. Introducción

Valida expone una API REST sobre HTTPS para validar personas naturales y jurídicas contra listas SARLAFT. Cada consulta se persiste 10 años con hash de integridad y puede descargarse como PDF auditable. La API es servidor a servidor: la API key nunca debe vivir en un navegador ni en una app móvil.

  • Base URL: https://api.valida.metrikone.co. Todas las rutas empiezan por /api/v1/.
  • Formato: JSON UTF-8 en ambos sentidos, salvo los PDF (application/pdf).
  • Timestamps: ISO 8601 en UTC (2026-09-08T14:32:00Z). Los periodos de consumo van en fechas locales de Bogotá (2026-09-01).
  • Request id: toda respuesta trae X-Valida-Request-Id. Guárdelo en sus logs; es lo primero que pide soporte.
  • CORS: las rutas /api/v1/* responden preflight, pero exponer la key en un navegador va contra el contrato. Llame siempre desde su backend.
  • Sin rate limit por ritmo: hoy no existe un 429. Lo que sí existe es la guía de concurrencia; respétela.

Qué es y qué no es Valida

Valida es una herramienta tecnológica de consulta automatizada. No constituye asesoría jurídica ni sustituye el sistema SARLAFT/SAGRILAFT/SIPLAFT del sujeto obligado. El análisis de coincidencias, la decisión de vinculación y el reporte a la UIAF siguen siendo del oficial de cumplimiento. OFAC SDN, Unión Europea y US State Dept FTO se consultan como referencia: no son vinculantes en Colombia.

2. Quickstart en 5 pasos

  1. Genere su API key en el portal de clientes. MeTRIK le da acceso a /portal a la persona que administra la integración; ahí la genera, y se muestra una sola vez. Guárdela como secreto de su backend (variable de entorno VALIDA_API_KEY). No se puede recuperar; si se pierde, se regenera.
  2. Compruebe que llega.
    curl -s https://api.valida.metrikone.co/api/v1/health
  3. Haga su primera validación con una Idempotency-Key desde el día uno.
    curl -s -X POST https://api.valida.metrikone.co/api/v1/validate \
      -H "Authorization: Bearer $VALIDA_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: $(uuidgen)" \
      -d '{
        "tipo": "natural",
        "nombre": "Juan Perez Gomez",
        "documento": { "tipo": "CC", "numero": "1077089147" },
        "referencia_externa": "EXP-2026-00123"
      }'
    Lea severidad, matches[] y listas_consultadas[]. Guarde consulta_id.
  4. Descargue el PDF auditable de esa consulta.
    curl -s https://api.valida.metrikone.co/api/v1/reporte/<consulta_id> \
      -H "Authorization: Bearer $VALIDA_API_KEY" -o reporte.pdf
  5. Mire su consumo y configure alertas.
    curl -s https://api.valida.metrikone.co/api/v1/cuenta/consumo -H "Authorization: Bearer $VALIDA_API_KEY"
    
    curl -s -X PUT https://api.valida.metrikone.co/api/v1/cuenta/alertas \
      -H "Authorization: Bearer $VALIDA_API_KEY" -H "Content-Type: application/json" \
      -d '{ "umbrales": [80, 100], "emails": ["cumplimiento@suempresa.co"], "webhook": { "url": "https://suempresa.co/valida/webhook" } }'
    La respuesta del PUT trae webhook.secret una sola vez. Guárdelo.

Si su cliente API opera para terceros (agregador)

Agregue sujeto_obligado: { "razon_social", "nit" } a cada consulta. Sin él recibe400 sujeto_obligado_requerido. El PDF se emite a nombre de ese sujeto obligado y usted queda como canal. Ver sujeto obligado final.

3. Autenticación y rotación de key

Toda ruta /api/v1/* exige Authorization: Bearer <api_key>. Valida guarda solo el hash SHA-256 de la key: nadie en MeTRIK puede leerla después de emitida.

Authorization: Bearer <api_key>
Content-Type: application/json
  • Key ausente, mal formada, revocada, vencida o de cliente inactivo: 401 invalid_api_key con header WWW-Authenticate: Bearer.
  • Dónde se obtiene: en el portal de clientes. El acceso es por invitación de MeTRIK, sin registro abierto: la persona invitada entra con su correo y un código de un solo uso. Desde ahí lista las keys de su empresa (prefijo, nombre, creación, último uso y estado), las genera, las regenera y las revoca. Toda operación queda registrada con usuario, hora e IP.
  • Rotación sin corte: al regenerar una key, la anterior sigue funcionando 24 horas y después responde 401. Cambie la key en su integración dentro de ese plazo. Si cree que la key se filtró, regenere revocando la anterior de inmediato, o revóquela sola.
  • Una key por integración. Si varios sistemas suyos consultan, genere una key por sistema: permite revocar una sin tumbar las demás. Hay un máximo de 5 keys activas por cliente; las que están en sus 24 horas de retiro no cuentan.
  • Nunca la envíe en la URL, en un navegador ni en logs.

4. Idempotencia y reintentos

Cada POST /validate que se persiste es una consulta cobrable. Un timeout de su lado con un reintento a ciegas produce dos consultas cobradas. Para evitarlo, envíe siempre el header Idempotency-Key.

SituaciónQué devuelve Valida
Idempotency-KeyDe 1 a 64 caracteres ASCII imprimibles, único por consulta. Recomendado: UUID v4. Vale 24 horas.
Misma clave + mismo cuerpo en 24 hLa respuesta original, mismo consulta_id, header X-Valida-Idempotent-Replay: true. No se cobra.
Misma clave + cuerpo distinto409 idempotency_error. Use otra clave.
Clave mal formada400 idempotency_key_invalida.
Respuesta 4xx o 5xxNo reserva la clave: puede reintentar con la misma.

Reintentos recomendados

  • Timeout del cliente: 60 segundos o más. Lo normal son 1-4 s, pero una consulta con muchos candidatos puede tardar más; el servidor corta a los 60 s.
  • Reintente ante timeout, error de red y 500 persistencia_error, siempre con la misma Idempotency-Key, con espera exponencial (2 s, 4 s, 8 s) y máximo 3 intentos.
  • No reintente 400, 401, 402 ni 409: la respuesta no va a cambiar hasta que usted cambie algo.
  • referencia_externa: mande su id de expediente o fila; se devuelve tal cual y luego puede buscarlo con GET /consultas?referencia_externa=. Es para conciliar; no reemplaza la Idempotency-Key.

5. Listas, tiers y versión de lista

TierListasQué significa para usted
1_vinculanteONU Consolidated · CSN ColombiaObligación legal explícita en Colombia. Un exacto aquí es un hallazgo alto.
2_obligatoriaPEP Colombia (SIGEP)Obligatoria de construir por el sujeto obligado. Un exacto es hallazgo medio: activa debida diligencia intensificada, no bloqueo.
3_referenciaOFAC SDN · EU Consolidated Sanctions · US State Dept FTONo vinculantes en Colombia. Estándar de facto de debida diligencia; su tratamiento lo define el manual SARLAFT de cada sujeto obligado.

Valida descarga cada lista de su fuente oficial a diario y la versiona por hash. Cada respuesta declara contra qué versión se comparó y esa versión queda grabada con la consulta:

  • listas_consultadas[] en el cuerpo: slug, nombre, tier, version_id, hash_contenido, fetched_en, total_entradas.
  • Header X-Valida-Listas-Version: onu_consolidated=2026-09-05T05:15:12Z;ofac_sdn=….
  • El PDF de /reporte/{consulta_id} imprime esas mismas versiones aunque lo descargue meses después. Nunca se recalcula.

Para el oficial de cumplimiento de su cliente

Guarde listas_consultadas junto con el resultado. Es la evidencia de que la verificación se hizo contra la lista vigente ese día.

6. Valida Diligencia (módulo aparte)

POST /v1/diligencia consulta antecedentes de fuentes nacionales para debida diligencia ampliada. Es un módulo distinto, no una lista más de SARLAFT, y por eso vive en su propio endpoint. Está en beta y se cobra aparte del paquete SARLAFT.

No mezcle los dos módulos

Un hallazgo de Diligencia no es un hallazgo SARLAFT. No entra en /validate, no aparece en listas_consultadas[], no sale en el PDF ni en el certificado mensual, y no descuenta del paquete SARLAFT. Cada respuesta viene sellada como REFERENCIA — decisión del cliente. Tratarlo como sanción LA/FT/FP ante un supervisor es un error de fondo del sujeto obligado.

FuenteQué contieneQué NO es
siri_procuraduriaSubconjunto certificable del SIRI publicado como dato abierto por la Procuraduría General de la Nación: sanciones disciplinarias e inhabilidades.No equivale ni reemplaza al certificado de antecedentes disciplinarios que expide la Procuraduría.
secop2_multasMultas, sanciones e inhabilidades a contratistas en SECOP II (Ley 80/1993 y Ley 1150/2007).Parte de los registros figura «A la espera de aprobación»: lea estado_plataforma antes de concluir.

Ambas fuentes son datos abiertos de datos.gov.co bajo licencia CC BY-SA 4.0, se ingieren con el mismo cron diario y se versionan por hash igual que las listas SARLAFT. La respuesta trae fuentes_consultadas[] con la versión de cada una.

curl -X POST https://api.valida.metrikone.co/api/v1/diligencia \
  -H "Authorization: Bearer $VALIDA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "documento": "1020304050",
    "tipo_documento": "CC",
    "nombre": "Juan Pérez Gómez",
    "referencia_externa": "exp-4471"
  }'
{
  "consulta_id": "…",
  "modulo": "diligencia",
  "tipo": "referencia",
  "sello": "REFERENCIA — decisión del cliente",
  "leyenda": "Información de referencia para debida diligencia. No constituye lista de sancionados LA/FT/FP ni hallazgo SARLAFT. La decisión es del cliente.",
  "total_hallazgos": 1,
  "fuentes_consultadas": [
    { "slug": "siri_procuraduria", "version_id": "…", "hash_contenido": "…", "fetched_en": "…" }
  ],
  "hallazgos": [ { "fuente": "siri_procuraduria", "coincidencia_por": "documento", "score": 1 } ],
  "hash_respuesta": "…",
  "fecha_consulta": "…"
}
  • Autenticación: la misma api_key. No hay credencial aparte.
  • Entrada: documento obligatorio (mínimo 5 caracteres); tipo_documento, nombre y referencia_externa opcionales.
  • Errores: mismo contrato que el resto de la API (error, message, doc_url, request_id).
  • Consumo: el contador y el cobro de Diligencia son independientes y todavía no están expuestos en /cuenta/consumo. Mientras tanto, cada consulta queda registrada como facturable.

7. Sujeto obligado final (agregadores)

Si su plataforma consulta por cuenta de varias empresas (patrón agregador), el reporte no puede quedar a nombre de su plataforma: un supervisor concluiría que el obligado no consultó. Por eso existe sujeto_obligado.

CampoRegla
sujeto_obligado.razon_socialRazón social legal del sujeto obligado final. 3 a 200 caracteres.
sujeto_obligado.nitNIT. Se aceptan puntos y guion con dígito de verificación ("800.123.456-7"); se guarda normalizado a dígitos.
Cuándo es obligatorioCuando su cliente API está marcado como opera para terceros. Si falta: 400 sujeto_obligado_requerido. Si no está marcado, es opcional y se estampa igual si lo envía.
Efecto en el PDFLa banda principal dice "Sujeto obligado: <razón social> — NIT" y debajo "A través de <su razón social> — NIT". El pie repite al sujeto obligado en cada página.
BúsquedaGET /consultas?sujeto_obligado_nit=800123456 devuelve solo las consultas de ese sujeto obligado.
{
  "tipo": "juridica",
  "nombre": "Acme Trading SAS",
  "documento": { "tipo": "NIT", "numero": "900123456" },
  "sujeto_obligado": { "razon_social": "Transportes La Sabana SAS", "nit": "800.123.456-7" },
  "referencia_externa": "vinculacion-8841"
}

8. Consumo: paquete mensual o bolsa

Su contrato tiene una de dos modalidades, y el header X-Valida-Modalidad (y el campo modalidad de/cuenta/consumo) le dice cuál. En las dos cuenta toda consulta que se persiste (una respuesta 200 de/validate que no sea replay).

  • Paquete mensual: consultas por mes calendario (America/Bogotá). El contador vuelve a cero el día 1 y no hay rollover: lo que no se usa en el mes no pasa al siguiente.
  • Bolsa prepagada: un número de consultas con fecha de vencimiento y sin reinicio mensual. Usted consume a su ritmo y compra otra bolsa cuando la necesite. Vea la sección siguiente.

Paquetes mensuales

PaqueteConsultas/mesPrecio mes (COP + IVA)UnitarioExcedente por consulta
1K1.000$150.000$150$110
5K5.000$550.000$110$90
10K10.000$900.000$90$70
20K20.000$1.400.000$70$50
A medida+20.000Según contratoSegún contrato$50

El excedente se cobra al unitario del escalón siguiente y entra en la cuenta del siguiente periodo.

Bolsa prepagada

  • Saldo = consultas compradas − consultas consumidas desde la activación. No se reinicia por mes.
  • Vencimiento: la bolsa vence 6 meses después de la entrega de credenciales (campo vence_en).
  • Corte: la bolsa se corta con lo primero que ocurra, se agota o vence. Cortada, POST /validate responde 402 bolsa_agotada o 402 bolsa_vencida y la consulta rechazada no se registra ni se cobra. Las demás rutas (consultas, reportes, cuenta) siguen funcionando.
  • Sin excedente ni arrastre: no se puede consumir por encima de la bolsa, ni siquiera con peticiones en paralelo, y lo que no se use antes del vencimiento no pasa a la siguiente bolsa.
  • Recompra: cuando MeTRIK registra el pago de una bolsa nueva, esa bolsa queda activa de inmediato, le llega paquete.renovado y los avisos vuelven a medirse contra la bolsa nueva. Reintente las consultas que recibieron 402 con su misma Idempotency-Key.
  • Una bolsa se abre con el pago ya registrado, así que no hay bloqueo por mora (cobro y bloqueo por mora aplica solo al paquete mensual).

Headers en cada respuesta de /validate

Van en el 200 y en los 4xx de negocio (400, 402, 409). Son la misma lectura que alimenta /cuenta/consumo: no cuestan una llamada extra.

X-Valida-Modalidad:           mensual | bolsa
X-Valida-Plan:                20K   (bolsa, si la modalidad es bolsa)
X-Valida-Periodo-Inicio:      2026-09-01   (solo mensual)
X-Valida-Periodo-Fin:         2026-10-01   (solo mensual)
X-Valida-Bolsa-Activada-En:   2026-09-14T20:38:51Z   (solo bolsa)
X-Valida-Bolsa-Vence-En:      2027-03-14T20:38:51Z   (solo bolsa)
X-Valida-Consumo-Incluidas:   20000   (en bolsa: compradas)
X-Valida-Consumo-Consumidas:  16421
X-Valida-Consumo-Restante:    3579   (en bolsa: saldo)
X-Valida-Consumo-Excedente:   0
X-Valida-Consumo-Estado:      ok | umbral_80 | agotado | excedente | bloqueado | sin_plan | vencido
X-Valida-Listas-Version:      onu_consolidated=2026-09-05T05:15:12Z;ofac_sdn=2026-09-07T05:16:03Z;...

Endpoints de cuenta

  • GET /api/v1/cuenta/consumo: contador, estado de cobro y política; con paquete mensual trae plan y periodo, con bolsa trae bolsa y periodo: null. 409 sin_plan_asignado si su cliente no tiene paquete ni bolsa (por ejemplo, una beta sin costo); /validate funciona igual.
  • GET /api/v1/cuenta/consumo/historial?periodos=6: periodos anteriores con consumidas, excedente y estado de cobro; con bolsa, sus bolsas (modalidad: "bolsa", bolsas[]).

Paquete mensual: al 100% no pasa nada malo

En paquete mensual Valida no bloquea por consumo. La consulta 20.001 se procesa igual, se marca como excedente (X-Valida-Consumo-Estado: excedente) y se cobra al unitario del escalón siguiente. Un tope duro por consumo solo existe si su empresa lo pide por escrito.

Bolsa prepagada: al llegar a cero, 402

Con bolsa, la consulta que ya no cabe recibe 402 bolsa_agotada, y una bolsa vencida recibe 402 bolsa_vencidaaunque le quede saldo. Trátelo como un error de negocio: avise a su área de compras y no reintente en bucle. Configure alertas para enterarse antes: por saldo (80 y 100 por defecto) y por vencimiento (30, 7 y 1 días).

9. Cobro y bloqueo por mora

En lenguaje claro: nunca se bloquea por consumo; solo por cuenta vencida sin pago. Esta sección aplica al paquete mensual: una bolsa prepagada se abre con el pago ya registrado y no tiene cuenta que vencer. Así funciona el ciclo mensual:

  1. Aviso de cobro. Cuando el consumo llega al 80% del paquete o cuando llega el día 20 del periodo (lo primero que ocurra), MeTRIK emite la cuenta de cobro del siguiente paquete. Usted recibe el evento paquete.cobro_emitido (si configuró alertas) con la fecha de vencimiento.
  2. Pago. Cuando MeTRIK registra el pago, recibe paquete.reactivado si estaba bloqueado. Si no lo estaba, no pasa nada visible: siguió consultando todo el tiempo.
  3. Bloqueo. Solo si se cumplen las dos condiciones a la vez: la cuenta de cobro venció sin pago registrado y el consumo del periodo supera el 100% del paquete. Entonces POST /validate responde 402 paquete_vencido_sin_pago con X-Valida-Consumo-Estado: bloqueado. Las demás rutas (consultas, reportes, cuenta) siguen funcionando.
  4. Reactivación. Se envía el soporte de pago a soporte. Al registrarlo, el siguiente /validate vuelve a funcionar. Ninguna consulta se pierde: reintente las que recibieron 402 con su misma Idempotency-Key.
Respuesta 402
{
  "error": "paquete_vencido_sin_pago",
  "message": "La cuenta de cobro del paquete esta vencida sin pago registrado y el consumo supera el paquete contratado. Registre el pago con soporte para reactivar; las consultas no se pierden.",
  "doc_url": "https://app.valida.metrikone.co/docs#error-paquete_vencido_sin_pago",
  "request_id": "req_3f9c1a2b4d5e6f708192a3b4",
  "consumo": { "consumidas": 21340, "incluidas": 20000, "excedente": 1340, "estado": "bloqueado", ... },
  "cobro": { "cuenta_vence_en": "2026-10-05T00:00:00Z", "cuenta_referencia": "CC-0042", "periodo": "2026-09-01" },
  "como_reactivar": "Envie el soporte de pago a mauricio.moreno@metrik.com.co o al WhatsApp +57 315 950 9103. ..."
}

Cómo programar el 402

Trátelo como un error de negocio, no de red: registre el request_id, avise a su área de cartera y no reintente en bucle. Puede sondear GET /cuenta/consumo (campo cobro.bloqueado) cada pocos minutos o esperar el evento paquete.reactivado.

10. Alertas y webhooks

Configure umbrales de consumo y canales (email y/o webhook). Por defecto los umbrales son 80 y 100 y no hay canal configurado: hasta que no ponga un email o una URL, no recibe nada. Con bolsa prepagada los umbrales se miden contra las consultas compradas de la bolsa vigente (el 80% avisa cuando le queda el 20% del saldo) y vuelven a dispararse en cada bolsa nueva; además llega paquete.vencimiento_proximo 30, 7 y 1 días antes del vencimiento (fijos, no se configuran) ypaquete.vencido al vencer. El cuerpo del evento trae modalidad: "bolsa", el objeto bolsa y periodo: null.

RutaQué hace
GET /cuenta/alertasConfiguración vigente y eventos del periodo (disparados con su estado de entrega, y pendientes).
PUT /cuenta/alertasActualiza umbrales (1-200 %, máximo 6), emails, webhook.url (https) y activa. Los campos omitidos se conservan. Al configurar la URL por primera vez la respuesta trae webhook.secret una sola vez.
POST /cuenta/alertas/webhook/rotar-secretoSecreto nuevo (una sola vez). El anterior sigue firmando 24 h.
POST /cuenta/alertas/pruebaEnvía paquete.prueba a sus canales y devuelve el evento_id y el resultado del primer intento.

Eventos

EventoCuándoIdempotencia
paquete.umbral_alcanzadoPrimera consulta del periodo (o de la bolsa) que cruza cada umbral < 100 que usted configuró.Una vez por (periodo o bolsa, umbral).
paquete.agotadoPrimera consulta que cruza el 100%. En bolsa, desde ahí /validate responde 402.Una vez por periodo o por bolsa.
paquete.excedenteCada umbral > 100 configurado (110, 120, 150…).Una vez por (periodo, umbral).
paquete.renovadoMensual: primera consulta del periodo nuevo, el contador volvió a cero. Bolsa: se activó una bolsa nueva.Una vez por periodo o por bolsa.
paquete.cobro_emitidoMeTRIK marcó la cuenta de cobro del siguiente paquete, con vencimiento.Una vez por periodo.
paquete.bloqueadoLa primera consulta que recibe 402 en el periodo (mensual, por mora) o en la bolsa (agotada o vencida; el cuerpo trae motivo).Una vez por periodo o por bolsa.
paquete.reactivadoSe registró el pago y el bloqueo se levantó.Puede repetirse.
paquete.pruebaA petición.Puede repetirse.
paquete.vencimiento_proximoSolo bolsa: faltan 30, 7 o 1 días para el vencimiento (umbral = días). Si el aviso se retrasa, llega el que corresponde, no los que ya pasaron.Una vez por (bolsa, días).
paquete.vencidoSolo bolsa: la bolsa llegó a su vencimiento con saldo. Desde ahí /validate responde 402 bolsa_vencida.Una vez por bolsa.

Entrega

  • POST JSON a su URL, timeout 10 s. Éxito = cualquier 2xx. Responda rápido y procese después.
  • Reintentos con espera 1 min, 5 min, 30 min, 2 h, 12 h, 24 h. Tras 6 fallos el evento queda fallido (visible en GET /cuenta/alertas).
  • El email se envía en paralelo al webhook, una vez por evento.
  • Headers: X-Valida-Event (tipo), X-Valida-Delivery-Id (cambia en cada intento), X-Valida-Signature.
  • Deduplique por id del cuerpo: puede recibir el mismo evento más de una vez.
Cuerpo de ejemplo · paquete.agotado
{
  "id": "0d6f2c3a-5b1e-4e8a-9c7d-2f1a3b4c5d6e",
  "tipo": "paquete.agotado",
  "creado_en": "2026-09-18T15:41:02Z",
  "version": "2026-09-01",
  "cliente_id": "55a4400c-...",
  "periodo": { "inicio": "2026-09-01", "fin": "2026-10-01" },
  "umbral": 100,
  "consumo": { "consumidas": 20000, "incluidas": 20000, "restantes": 0, "porcentaje": 100, "excedente": 0, "excedente_precio_unitario": 50 },
  "plan": { "codigo": "20K", "consultas_incluidas": 20000 },
  "politica": { "bloqueo_por_consumo": false, "bloqueo_por_mora": true, "rollover": false },
  "consulta_id_detonante": "c0a8..."
}

Verificar la firma

X-Valida-Signature: t=1758210062,v1=<hex>. La firma es hex(HMAC-SHA256(secret, t + "." + cuerpo_crudo)). Durante las 24 h siguientes a una rotación hay dos v1: acepte si cualquiera coincide. Compare en tiempo constante y rechace si|ahora − t| > 300 s. Use el cuerpo crudo (bytes), no el JSON re-serializado.

# Firma manual para probar con su secreto (t = ahora en segundos)
T=$(date +%s); BODY='{"id":"evt_test","tipo":"paquete.prueba"}'
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$VALIDA_WEBHOOK_SECRET" | awk '{print $2}')
curl -s -X POST https://suempresa.co/valida/webhook \
  -H "Content-Type: application/json" -H "X-Valida-Event: paquete.prueba" \
  -H "X-Valida-Signature: t=$T,v1=$SIG" -d "$BODY"

11. Concurrencia para lotes

No hay endpoint de lote: un cargue de 9.000 filas son 9.000 llamadas a /validate. Está bien, siempre que las haga con cabeza. Esta es la guía que Valida aguanta hoy y que MeTRIK monitorea:

  • Hasta 20 peticiones en vuelo por cliente API. Más no es más rápido: entran a la misma base de datos y se estorban.
  • Una Idempotency-Key por fila (por ejemplo {lote}-{fila} o un UUID guardado con la fila). Reintente solo con esa clave.
  • Timeout 60 s por petición y reintento exponencial (2 s, 4 s, 8 s) ante timeout, error de red y 500.
  • Pare el lote si recibe 401 o 402: ninguna fila siguiente va a pasar.
  • Guarde por fila consulta_id, severidad y listas_consultadas. Con los consulta_id puede pedir un solo PDF agregado en POST /reporte-lote (hasta 500 por PDF).
  • Horario: las listas se actualizan a las 05:15 de Bogotá; en esa ventana una consulta puede tardar un poco más. Si puede elegir, corra los lotes grandes fuera de esa hora.
# 20 en paralelo con GNU parallel; cada linea del CSV: referencia,tipo,nombre,doc_tipo,doc_numero
parallel -j 20 --colsep ',' \
  'curl -s -X POST https://api.valida.metrikone.co/api/v1/validate \
     -H "Authorization: Bearer $VALIDA_API_KEY" -H "Content-Type: application/json" \
     -H "Idempotency-Key: lote-2026-09-08-{1}" \
     -d "{\"tipo\":\"{2}\",\"nombre\":\"{3}\",\"documento\":{\"tipo\":\"{4}\",\"numero\":\"{5}\"},\"referencia_externa\":\"{1}\"}" \
     > resultados/{1}.json' :::: filas.csv

12. Formato de error y códigos

Toda respuesta de error, en cualquier ruta /api/v1/*, tiene la misma forma:

{
  "error": "validation_error",           // codigo estable: programe contra este
  "message": "El cuerpo de la peticion no cumple el esquema. Revise el campo issues.",
  "doc_url": "https://app.valida.metrikone.co/docs#error-validation_error",
  "request_id": "req_3f9c1a2b4d5e6f708192a3b4",   // mismo valor del header X-Valida-Request-Id
  "issues": [ { "path": ["nombre"], "message": "..." } ]   // solo en validation_error
}
HTTPerrorCuándoQué hacer
400invalid_jsonEl cuerpo no es JSON válido.Corrija el cuerpo. No reintente igual.
400validation_errorEl JSON no cumple el esquema (falta tipo, ni nombre ni documento, NIT inválido…).Lea issues[] (path + message) y corrija ese campo.
400sujeto_obligado_requeridoSu cliente opera para terceros y la consulta no trae sujeto_obligado.Agregue { razon_social, nit } del sujeto obligado final.
400idempotency_key_invalidaIdempotency-Key vacía, con espacios o de más de 64 caracteres.Use un UUID v4.
400consulta_ids_requeridosPOST /reporte-lote sin consulta_ids.Envíe al menos un consulta_id.
400maximo_500_consultas_por_loteMás de 500 consulta_ids en un PDF.Parta el lote.
401invalid_api_keyKey ausente, mal formada, revocada, vencida (regenerada hace más de 24 horas) o de cliente inactivo.Revise el header Authorization. Si se regeneró, use la nueva. Pare el lote.
402paquete_vencido_sin_pagoPaquete mensual: cuenta de cobro vencida sin pago registrado Y consumo por encima del 100%.Avise a cartera y registre el pago con soporte. No reintente en bucle; el 402 no cambia solo.
402bolsa_agotadaBolsa prepagada: se usaron todas las consultas compradas (o una consulta en paralelo se llevó la última).Compre una bolsa nueva con soporte. La consulta rechazada no se registró ni se cobró; reintente con la misma Idempotency-Key al reactivarse.
402bolsa_vencidaBolsa prepagada: llegó a su fecha de vencimiento, aunque quede saldo.Compre una bolsa nueva con soporte. Lo no usado de la bolsa vencida no pasa a la siguiente.
404not_foundLa consulta no existe o no es de su cliente.Verifique el consulta_id.
404consultas_no_encontradas_para_clienteNinguno de los consulta_ids del lote es suyo.Verifique los ids.
409idempotency_errorLa Idempotency-Key ya se usó con un cuerpo distinto.Use una clave nueva para una consulta distinta.
409sin_plan_asignadoGET /cuenta/consumo de un cliente sin paquete.Escriba a soporte para activarlo. /validate sigue funcionando.
500persistencia_errorLa consulta corrió pero no se pudo guardar. No se cobró.Reintente con la misma Idempotency-Key tras 2 s.
500db_errorError interno al leer o escribir.Reintente con espera exponencial. Si persiste, soporte con el request_id.

Lo que NO existe

No hay 429, Retry-After, 403 permiso_denegado ni 503. Si su cliente HTTP los maneja de forma genérica, bien; pero no programe lógica de negocio sobre ellos.

13. Endpoints con ejemplos

MétodoRutaPara qué
GET/api/v1/healthEstado del servicio (sin auth).
POST/api/v1/validateValidar una persona o entidad contra las listas SARLAFT.
POST/api/v1/diligenciaAntecedentes nacionales de referencia. Módulo aparte, ver §6.
GET/api/v1/consultasListar y filtrar sus consultas.
GET/api/v1/reporte/{consulta_id}PDF auditable de una consulta.
POST/api/v1/reporte-lotePDF agregado de hasta 500 consultas.
GET/api/v1/cuenta/consumoContador del paquete y estado de cobro.
GET/api/v1/cuenta/consumo/historialPeriodos anteriores.
GET / PUT/api/v1/cuenta/alertasVer y configurar alertas.
POST/api/v1/cuenta/alertas/webhook/rotar-secretoRotar el secreto del webhook.
POST/api/v1/cuenta/alertas/pruebaProbar los canales.

Las rutas /api/v1/kyc/* y /api/v1/compliance/dual/* son internas de MeTRIK ONE y no forman parte del contrato con integradores. Las /api/admin/* son de operación de MeTRIK.

POST /api/v1/validate

curl -s -X POST https://api.valida.metrikone.co/api/v1/validate \
  -H "Authorization: Bearer $VALIDA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4c6d1a4e-0e0f-4c37-9c7d-6a5b6f1f2e3d" \
  -d '{
    "tipo": "natural",
    "nombre": "Juan Perez Gomez",
    "documento": { "tipo": "CC", "numero": "1077089147" },
    "fecha_nacimiento": "1985-03-15",
    "pais": "CO",
    "referencia_externa": "EXP-2026-00123",
    "sujeto_obligado": { "razon_social": "Transportes La Sabana SAS", "nit": "800123456" }
  }' -D -
Respuesta 200 (resumida)
{
  "consulta_id": "4f8e2a91-1c3e-4f0a-9d2b-1a2b3c4d5e6f",
  "severidad": "sin_hallazgo",
  "total_matches": 0,
  "consulta_parcial": false,
  "matches": [],
  "hash_reporte": "a3f8d92e4b1c...",
  "consultado": { "tipo": "natural", "nombre": "Juan Perez Gomez", "documento": { "tipo": "CC", "numero": "1077089147" } },
  "sujeto_obligado": { "razon_social": "Transportes La Sabana SAS", "nit": "800123456" },
  "referencia_externa": "EXP-2026-00123",
  "listas_consultadas": [
    { "slug": "onu_consolidated", "nombre": "ONU Consolidated Sanctions List", "tier": "1_vinculante",
      "version_id": "…", "hash_contenido": "…", "fetched_en": "2026-09-05T05:15:12Z", "total_entradas": 1002 },
    ...
  ],
  "fecha_reporte": "2026-09-08T14:32:00.512Z",
  "cliente": "Plataforma Ejemplo"
}
Un match (elemento de matches[])
{
  "lista": "ofac_sdn",
  "lista_nombre": "OFAC Specially Designated Nationals (SDN)",
  "tier": "3_referencia",
  "vinculante_colombia": false,
  "nombre_coincidencia": "PEREZ GOMEZ, Juan",
  "score": 0.97,
  "resultado": "exacto",
  "fundamento_legal": "SDNTK",
  "match_por": "nombre",
  "documento_coincidencia": { "tipo": "Cedula No.", "numero": "1077089147", "pais": "Colombia" },
  "derivado": false,
  "derivado_de": null
}

GET /api/v1/consultas

curl -s "https://api.valida.metrikone.co/api/v1/consultas?limite=50&desde=2026-09-01T00:00:00Z&severidad=alto&sujeto_obligado_nit=800123456" \
  -H "Authorization: Bearer $VALIDA_API_KEY"

GET /api/v1/reporte/{consulta_id}

curl -s https://api.valida.metrikone.co/api/v1/reporte/4f8e2a91-1c3e-4f0a-9d2b-1a2b3c4d5e6f \
  -H "Authorization: Bearer $VALIDA_API_KEY" -o reporte.pdf

POST /api/v1/reporte-lote

curl -s -X POST https://api.valida.metrikone.co/api/v1/reporte-lote \
  -H "Authorization: Bearer $VALIDA_API_KEY" -H "Content-Type: application/json" \
  -d '{ "consulta_ids": ["4f8e2a91-…", "7b2c1d83-…"], "titulo": "Cargue plantilla septiembre 2026" }' -o lote.pdf

GET /api/v1/cuenta/consumo

curl -s https://api.valida.metrikone.co/api/v1/cuenta/consumo -H "Authorization: Bearer $VALIDA_API_KEY"
Respuesta 200 · paquete mensual
{
  "modalidad": "mensual",
  "cliente": { "cliente_id": "55a4400c-…", "nombre": "Plataforma Ejemplo" },
  "plan": { "codigo": "20K", "nombre": "Paquete 20.000 consultas/mes", "consultas_incluidas": 20000, "precio_mes": 1400000,
            "precio_unitario": 70, "precio_excedente": 50, "a_medida": false, "moneda": "COP", "iva_incluido": false },
  "periodo": { "inicio": "2026-09-01", "fin": "2026-10-01", "zona": "America/Bogota", "dias_restantes": 23, "dia_del_periodo": 8,
               "renovacion_en": "2026-10-01T00:00:00-05:00" },
  "consumo": { "consumidas": 16420, "incluidas": 20000, "restantes": 3580, "porcentaje": 82.1, "excedente": 0,
               "excedente_precio_unitario": 50, "excedente_valor_estimado": 0, "estado": "umbral_80", "actualizado_en": "2026-09-08T14:02:11Z" },
  "cobro": { "cuenta_emitida_en": "2026-09-07T16:10:00Z", "cuenta_vence_en": "2026-09-22T00:00:00Z", "cuenta_referencia": "CC-0042",
             "pago_registrado_en": null, "cuenta_vencida_sin_pago": false, "bloqueado": false },
  "politica": { "bloqueo_por_consumo": false, "bloqueo_por_mora": "Solo con cuenta de cobro vencida sin pago registrado Y consumo por encima del 100% del paquete.",
                "excedente": "Se cobra al precio unitario del escalon siguiente y entra en la cuenta del siguiente periodo.",
                "rollover": false, "ciclo": "Mes calendario America/Bogota", "umbrales_alerta": [80, 100] }
}

La respuesta de arriba es de un paquete mensual. Con bolsa prepagada:

Respuesta 200 · bolsa prepagada
{
  "modalidad": "bolsa",
  "cliente": { "cliente_id": "8c211c68-…", "nombre": "Plataforma Ejemplo" },
  "plan": null,
  "periodo": null,
  "bolsa": { "bolsa_id": "…", "secuencia": 1, "estado": "vigente", "bloqueada": false,
             "activada_en": "2026-09-14T20:38:51Z", "vence_en": "2027-03-14T20:38:51Z", "dias_para_vencer": 146,
             "consultas_compradas": 20000, "consumidas": 16420, "saldo": 3580,
             "precio_total": 1400000, "precio_unitario": 70, "moneda": "COP", "iva_incluido": false,
             "pago_referencia": "CC-0042", "pago_registrado_en": "2026-09-14T20:38:51Z" },
  "consumo": { "consumidas": 16420, "incluidas": 20000, "restantes": 3580, "porcentaje": 82.1, "excedente": 0,
               "excedente_precio_unitario": null, "excedente_valor_estimado": null, "estado": "umbral_80", "actualizado_en": "2026-10-20T14:02:11Z" },
  "cobro": { "cuenta_emitida_en": null, "cuenta_vence_en": null, "cuenta_referencia": "CC-0042",
             "pago_registrado_en": "2026-09-14T20:38:51Z", "cuenta_vencida_sin_pago": false, "bloqueado": false },
  "politica": { "bloqueo_por_consumo": true, "bloqueo_por_mora": false, "vence": true,
                "excedente": "No hay excedente: al agotarse la bolsa, POST /validate responde 402 bolsa_agotada hasta que se compre una nueva.",
                "vencimiento": "La bolsa vence en vence_en (6 meses desde la entrega de credenciales). Vencida, POST /validate responde 402 bolsa_vencida aunque quede saldo.",
                "rollover": false, "ciclo": "Bolsa prepagada: sin periodo mensual. Se corta con lo primero que ocurra: se agota o vence. Lo no usado no pasa a la siguiente bolsa.",
                "umbrales_alerta": [80, 100], "avisos_vencimiento_dias": [30, 7, 1] }
}

GET /api/v1/cuenta/consumo/historial

curl -s "https://api.valida.metrikone.co/api/v1/cuenta/consumo/historial?periodos=6" -H "Authorization: Bearer $VALIDA_API_KEY"

GET / PUT /api/v1/cuenta/alertas

curl -s https://api.valida.metrikone.co/api/v1/cuenta/alertas -H "Authorization: Bearer $VALIDA_API_KEY"

curl -s -X PUT https://api.valida.metrikone.co/api/v1/cuenta/alertas \
  -H "Authorization: Bearer $VALIDA_API_KEY" -H "Content-Type: application/json" \
  -d '{ "umbrales": [50, 80, 100, 120], "emails": ["cumplimiento@suempresa.co"], "webhook": { "url": "https://suempresa.co/valida/webhook" }, "activa": true }'

POST /api/v1/cuenta/alertas/webhook/rotar-secreto · POST /api/v1/cuenta/alertas/prueba

curl -s -X POST https://api.valida.metrikone.co/api/v1/cuenta/alertas/webhook/rotar-secreto -H "Authorization: Bearer $VALIDA_API_KEY"
curl -s -X POST https://api.valida.metrikone.co/api/v1/cuenta/alertas/prueba -H "Authorization: Bearer $VALIDA_API_KEY"

14. Glosario

TérminoSignificado
tierClasificación normativa de la lista: 1_vinculante (obligación legal en Colombia), 2_obligatoria (el sujeto obligado debe construirla: PEP), 3_referencia (no vinculante, estándar de facto: OFAC SDN, Unión Europea y US State Dept FTO).
matchUna entrada de una lista cuyo nombre, alias o documento coincide con lo consultado. Una consulta puede tener cero, uno o varios.
scoreSimilitud entre 0 y 1 del nombre consultado contra el nombre o alias de la entrada (Jaro-Winkler 70% + Levenshtein 30% sobre nombres normalizados). Un match por documento tiene score 1.
resultadoexacto (score ≥ 0.95) o posible (0.70 a 0.95). Un posible exige análisis del oficial de cumplimiento; no es una confirmación.
severidadResumen de la consulta: alto (exacto en tier 1), medio (exacto en tier 2 o 3), bajo (solo posibles), informativo, sin_hallazgo.
consulta_parcialLa consulta trajo solo nombre o solo documento. La cobertura puede ser menor; el PDF lo advierte.
match_por / derivadoPor qué campo coincidió (nombre o documento). derivado = salió de un segundo pase con el dato complementario que trajo la entrada (por ejemplo, un documento encontrado por nombre se revalidó contra las demás listas).
versión de listaDescarga concreta de una lista desde su fuente oficial, identificada por version_id, hash_contenido y fetched_en. Toda consulta registra las versiones contra las que corrió.
sujeto obligadoLa empresa que por ley debe hacer la verificación SARLAFT. Con un agregador, es el cliente final de la plataforma, no la plataforma.
periodoPaquete mensual: mes calendario en America/Bogotá. El contador vuelve a cero el día 1. Una bolsa no tiene periodo.
bolsa prepagadaConsultas compradas con vencimiento (6 meses desde la entrega de credenciales) y sin reinicio mensual. Se corta con lo primero: se agota o vence. Sin excedente y sin arrastre a la siguiente bolsa.
saldoEn una bolsa: consultas compradas menos consumidas. Nunca es negativo.
paquete / incluidasConsultas del mes cubiertas por el precio fijo del plan.
excedenteSolo paquete mensual: consultas por encima de las incluidas. Se cobran al unitario del escalón siguiente en la cuenta del siguiente periodo. No bloquean. Una bolsa no tiene excedente.
bloqueo por moraSolo paquete mensual: /validate responde 402 paquete_vencido_sin_pago con cuenta de cobro vencida sin pago registrado y consumo por encima del 100%.
corte de bolsaSolo bolsa: /validate responde 402 bolsa_agotada o 402 bolsa_vencida hasta que se registre el pago de una bolsa nueva.
Idempotency-KeyClave que usted elige por consulta para que un reintento devuelva la misma respuesta en vez de crear (y cobrar) otra.
request_idIdentificador de cada petición (header X-Valida-Request-Id y cuerpo de error). Cítelo en soporte.

15. Changelog

Sin publicar

  • Portal de clientes en /portal: la persona que MeTRIK invita genera, regenera y revoca las keys de su empresa sin pasar por soporte, y ve el consumo (del paquete mensual o de la bolsa prepagada) y sus alertas.
  • Una key regenerada sigue autenticando 24 horas y luego responde 401 invalid_api_key. Las keys que nadie regenera no cambian de comportamiento.

1.3.0 · 2026-09-14

  • Modalidad bolsa prepagada: consultas con vencimiento (6 meses desde la entrega de credenciales), que se cortan al agotarse o vencer, sin excedente ni arrastre. Ver bolsa prepagada.
  • Errores nuevos 402 bolsa_agotada y 402 bolsa_vencida en POST /validate; eventos nuevos paquete.vencimiento_proximo y paquete.vencido; estado de consumo vencido.
  • Campo modalidad (mensual | bolsa) en /cuenta/consumo, /cuenta/alertas y en el cuerpo de los webhooks; headers X-Valida-Modalidad, X-Valida-Bolsa-Activada-En y X-Valida-Bolsa-Vence-En.
  • Con bolsa, /cuenta/consumo devuelve bolsa y periodo: null en vez de 409; /cuenta/consumo/historial devuelve bolsas[]; /cuenta/alertas devuelve bolsa_actual. Los clientes con paquete mensual reciben la misma respuesta de antes más modalidad.

1.2.0 · 2026-09-09

  • Nuevo módulo POST /diligencia (beta) con dos fuentes nacionales: SIRI de la Procuraduría y multas de SECOP II. Aislado del motor SARLAFT: tabla propia, sello de referencia y consumo aparte. Ver §6.
  • El catálogo de listas queda separado por módulo. /validate y listas_consultadas[] solo devuelven listas SARLAFT; ninguna fuente de Diligencia puede entrar a un reporte SARLAFT.

1.1.0 · 2026-09-08

  • Formato de error uniforme { error, message, doc_url, request_id, issues? } y header X-Valida-Request-Id en toda respuesta. CORS (preflight) en /api/v1.
  • Idempotency-Key en POST /validate con replay 24 h y 409 idempotency_error. Campo referencia_externa y filtro en /consultas.
  • listas_consultadas[] y X-Valida-Listas-Version: la versión de cada lista queda grabada con la consulta y el PDF la lee de ahí.
  • sujeto_obligado en /validate (obligatorio para clientes que operan para terceros), estampado en el PDF; filtro sujeto_obligado_nit.
  • Contador de paquete: headers X-Valida-Plan / X-Valida-Periodo-* / X-Valida-Consumo-*, GET /cuenta/consumo y /cuenta/consumo/historial.
  • Cobro y bloqueo por mora: 402 paquete_vencido_sin_pago solo con cuenta vencida sin pago y consumo > 100%.
  • Alertas: GET/PUT /cuenta/alertas, rotación de secreto, prueba, webhooks firmados (X-Valida-Signature) con reintentos, eventos paquete.*.
  • Retención de consultas y evidencia: 10 años (antes 5).
  • Documentación: se retiran del contrato el 429 rate_limit, 403 permiso_denegado y 503 que nunca existieron, y el límite de "1000 consultas/día".

1.0.0-mvp · 2026-05

  • POST /validate, GET /consultas, GET /reporte/{id}, POST /reporte-lote, GET /health.

Política de versionado: los cambios compatibles (campos y headers nuevos, códigos de error nuevos) se publican aquí sin cambiar la ruta. Un cambio incompatible saldría como /api/v2 con al menos 90 días de convivencia.

16. Soporte

  • Email: mauricio.moreno@metrik.com.co
  • WhatsApp: +57 315 950 9103
  • Qué incluir: el request_id, la hora (UTC), la ruta y el código error. Con eso se ubica la petición en segundos.
  • Acceso al portal de clientes, activación de paquete, registro de pagos: por los mismos canales. Las keys se generan y regeneran en el portal, sin soporte.
  • Horarios, categorías y tiempos de respuesta: Política de Atención y Soporte y ANS.