Ir al contenido

Referencia de la API de CMP

Este documento describe cómo integrarse con la API de la Case Management Platform (CMP) de Guardline. Cubre los tokens de API, la apertura de casos en cada mesa de análisis (alertas de fraude, solicitudes MED, alertas AML y casos de onboarding), la idempotencia y el límite de solicitudes, cómo las decisiones de los analistas vuelven al sistema de origen, los webhooks salientes y el manejo de errores.

El modelo de integración sigue una separación clara de responsabilidades: el motor del integrador detecta y abre el caso, Guardline encola, distribuye, analiza y decide. Cualquier motor de decisión, interno o de terceros, puede abrir casos de forma programática. El CMP aplica entonces colas, SLA, distribución, cadenas de aprobación, sesiones de comité y la traza de auditoría exactamente igual que para los casos generados por los demás módulos de la Suite Guardline.

La integración involucra a cuatro participantes:

ParticipanteResponsabilidad
Sistema de origenDetecta un evento (alerta de transacción, solicitud MED, alerta AML, derivación de onboarding) y abre un caso a través de la API de ingesta
API de CMP de GuardlineValida, deduplica, enruta el caso a una mesa y una cola, calcula el SLA y lo distribuye a un analista
Consola de CMP de GuardlineConsola web donde los analistas, supervisores y administradores trabajan los casos y gestionan los tokens
Receptor (opcional)Endpoint del integrador que recibe los webhooks salientes de los eventos del flujo AML

Cada mesa tiene su propio endpoint de ingesta. Todos son POST, aceptan application/json y se autentican con un token de API con alcance sobre esa mesa.

MesaEndpointAlcance del tokenIdentificador del caso
Fraude/api/v1/ingest/fraudeingest:fraudeexternal_id
MED (mecanismo especial de devolución)/api/v1/ingest/medingest:medid, event_id, notification_id o med_id
AML (prevención del lavado de dinero)/api/v1/ingest/pldingest:pldalerta_externo_id
Onboarding, personas físicas (KYC) y personas jurídicas (KYB)/api/v1/ingest/kycingest:kycexecution_id (sobre de onboarding) o case_id (payload genérico)

Todas las demás rutas de la API de CMP (/api/v1/cases, /api/v1/queues, /api/v1/users y otras) pertenecen a la consola y se autentican con la cookie de sesión del analista. Esas rutas no forman parte del contrato de integración y no se documentan aquí, salvo los endpoints de administración de tokens descritos en Administración de tokens.

Todas las llamadas a la API siguen el patrón REST sobre HTTPS con payloads JSON.

EntornoURL base
Sandboxhttps://{instance}.cmp.dev.guardline.com.br
Producciónhttps://{instance}.cmp.prod.guardline.com.br

El valor {instance} se aprovisiona durante la configuración del integrador e identifica la infraestructura dedicada. Cada instancia tiene su propia base de datos y su propia aplicación, aisladas. Un token emitido en una instancia solo autentica contra esa instancia.

Las solicitudes HTTP se rechazan. Se exige HTTPS con TLS 1.2 o superior en todos los entornos.

Cada llamada de ingesta requiere el encabezado X-API-Key con un token de API.

X-API-Key: cmp_e188905ec47a80ec035fe31a3264bf1a5f9231f1353ea24717cf311740305f11

Los tokens tienen la forma cmp_ seguida de 64 caracteres hexadecimales. Los crea un administrador del CMP, ya sea en la consola (pantalla de tokens de API) o mediante los endpoints de administración de tokens. El valor en texto plano se muestra una sola vez, en la respuesta de creación. Internamente el CMP almacena el hash SHA-256 del token junto con un prefijo de visualización (los primeros 12 caracteres, por ejemplo cmp_e188905e) para que los administradores puedan reconocer un token sin exponerlo.

Cada token incluye:

AtributoDescripción
labelNombre en texto libre, por ejemplo fpp-producao u onp-webhook-kyc
scopeLa mesa sobre la que el token puede escribir. Ver Alcances
rate_limit_max, rate_limit_window_secondsLímite de solicitudes por token. Por defecto, 60 solicitudes cada 60 segundos
expires_in_daysVencimiento opcional. 0 (el valor por defecto) significa que el token nunca vence

Un problema de credenciales siempre responde 401 o 403, nunca 400:

EstadoMensajeCausa
401missing api keyEl encabezado X-API-Key no fue enviado o está vacío
401invalid api keyEl token es desconocido, fue desactivado, fue eliminado o venció
403token scope not authorized for this endpointEl token existe pero su alcance no cubre el endpoint

Un 400 significa que el token fue aceptado y que el cuerpo de la solicitud fue rechazado, por lo que reemitir el token no lo resuelve.

Cada llamada rechazada (clave ausente, clave inválida, alcance incorrecto, límite de solicitudes) se registra en la traza de auditoría del CMP con la IP de origen, la ruta y el motivo. Las ingestas exitosas también se registran, con el cuerpo completo.

AlcanceEndpoints permitidos
ingest:fraudePOST /api/v1/ingest/fraude
ingest:medPOST /api/v1/ingest/med
ingest:pldPOST /api/v1/ingest/pld
ingest:kycPOST /api/v1/ingest/kyc (los casos KYC y KYB comparten este endpoint y este alcance)
ingestLos cuatro endpoints de ingesta

Use el alcance más restringido que cubra el sistema de origen. El alcance genérico ingest existe para herramientas internas y no debe emitirse a un motor de producción que escribe en una sola mesa. Un token creado sin un alcance explícito recibe el alcance genérico ingest, que se acepta en los cuatro endpoints de mesa. Las cadenas de alcance no se validan al momento de la creación, por lo que un error de escritura produce un token que es rechazado en todos los endpoints.

El límite de solicitudes se aplica por token, por endpoint de mesa y por réplica de la API, en una ventana fija. El límite y la ventana son atributos del token (rate_limit_max y rate_limit_window_seconds, con valores por defecto de 60 solicitudes cada 60 segundos) y los elige el administrador al momento de la creación. Cada ruta de ingesta cuenta su propia ventana y cada réplica mantiene sus propios contadores en memoria, por lo que el techo efectivo es mayor que el número configurado cuando varias réplicas atienden el tráfico. Las ventanas superiores a 120 segundos no son confiables: una ventana se descarta dos minutos después de su último uso, independientemente de rate_limit_window_seconds. Dimensione el límite para el pico del sistema de origen, teniendo en cuenta que los reintentos con backoff del lado del emisor pueden producir ráfagas.

Cuando se supera el límite, la API responde 429 con el mensaje ingest rate limit exceeded. Espere a que transcurra la ventana y reintente; la solicitud no fue procesada.

El cuerpo de la solicitud está limitado a 1 MB por defecto, y el límite es configurable por instancia. Un cuerpo que supera el límite se rechaza con 413. Decida los reintentos a partir del estado HTTP y no del campo code: actualmente un cuerpo de tamaño excesivo reporta code: INTERNAL_ERROR junto con el 413.

El encabezado opcional X-Tenant-Slug identifica el tenant del sistema de origen y queda registrado en la traza de auditoría solo en las llamadas rechazadas, no en una ingesta exitosa. Cuando se omite, toma el valor default. No cambia el enrutamiento: la instancia es el límite de aislamiento.

Los endpoints de ingesta devuelven el cuerpo del recurso directamente, sin sobre. La forma es específica de cada endpoint y se documenta en cada sección; las tres mesas genéricas (fraude, MED, onboarding) comparten esta:

{
"case_id": "2ca0bd66-0ef9-44e6-9b47-6816264dd779",
"external_id": "fpp-case-001",
"created": true
}

Los errores usan un único sobre en todos los endpoints:

{
"error": true,
"message": "external_id é obrigatório",
"code": "BAD_REQUEST",
"request_id": "5c1e8f2a-7b3d-4c9e-a1f0-2d6b8e4a9c7f"
}
CampoDescripción
errorSiempre true en una respuesta de error
messageMotivo legible por personas. Los mensajes de validación están en portugués o en inglés según la mesa
codeCódigo estable legible por máquina. Ver Códigos de error
request_idIdentificador de correlación, devuelto también en el encabezado de respuesta X-Request-ID. Cítelo al contactar con soporte

Fuera de producción el sobre incluye además un campo details con el error interno sin procesar. En producción el campo se omite.

Cada endpoint de ingesta es idempotente sobre una clave natural elegida por el sistema de origen, con alcance a la mesa:

MesaClave natural
Fraudeexternal_id
MEDid, event_id, notification_id o med_id (el primero presente); en su defecto, el SHA-256 del cuerpo sin procesar, almacenado y devuelto como sha256: seguido del hash
AMLalerta_externo_id
Onboardingexecution_id (sobre de onboarding) o case_id (payload KYC genérico)

La primera llamada para una clave crea el caso y responde 201. Cualquier llamada posterior con la misma clave responde 200 con created: false (o is_new_case: false en la mesa AML) y el identificador del caso existente. Nada se sobrescribe: el caso es inmutable tras su creación y el trabajo del analista nunca se reemplaza por un reenvío. Esto hace que sea seguro reintentar los endpoints tras un tiempo de espera de red sin generar trabajo duplicado para la mesa.

La ventana de idempotencia no tiene límite: la clave permanece única durante toda la vida de la mesa.

POST /api/v1/ingest/fraude

Alcance: ingest:fraude. Abre un caso en la mesa Fraude a partir de una alerta de transacción producida por un motor de detección de fraude (el FPP de Guardline o un motor de terceros).

El cuerpo es un objeto JSON. Solo tres campos son obligatorios; todo lo demás enriquece el caso. Los nombres de clave alternativos aceptados para el mismo campo se listan en la columna de alias. El cuerpo completo se conserva como payload del caso, por lo que las claves que no figuran aquí se mantienen y quedan visibles para el analista aunque no estén indexadas.

CampoTipoObligatorioAliasDescripción
external_idstringsíid, event_idIdentificador único de la alerta en el sistema de origen. Clave de idempotencia
subject_documentstringsídocumentoCPF o CNPJ del cliente investigado. A diferencia de la mesa AML, el formato no se valida: no se elimina la puntuación ni se verifica la cantidad de dígitos, por lo que el valor se almacena exactamente como se envía y quien realiza la llamada debe normalizarlo
transaction_amountnumbersíamount, valorImporte en BRL, debe ser mayor que cero. Se almacena en centavos
subject_namestringnocliente_nome, nomeNombre del cliente
counterparty_namestringnocontraparte_nomeNombre de la contraparte (beneficiario)
transaction_typestringnotipo_transacaoTipo de transacción, en minúsculas. Por defecto, pix
risk_levelstringnoriscoSolo se reconocen low, medium, high y critical. Se deriva de score cuando está ausente o vacío; cualquier otro valor deja el caso sin nivel de riesgo
scoreintegernoScore del motor en una escala de 0 a 1000. Ver Score, riesgo y prioridad
priorityintegernoDe 0 a 9, cuanto menor más urgente. Por defecto, 3. Los valores fuera del rango se ajustan al límite
sla_deadlinestring (RFC 3339)noPlazo de SLA explícito. Cuando está ausente, el plazo proviene de la política de SLA configurada para la mesa, y el caso se crea sin plazo cuando no aplica ninguna política. Un valor que no se puede interpretar se ignora en silencio y en su lugar se aplica la política de la mesa; no se devuelve ningún 400
fraud_analysisobjectnoExplicación completa de la decisión y contexto del cliente. Ver El bloque fraud_analysis

La mesa Fraude espera el score del motor en la escala del FPP (de 0 a 1000). El CMP almacena score / 10, limitado a 100, en el caso para que las colas y los dashboards compartan una única escala entre mesas. La división es una división entera, por lo que el resultado se trunca y no se redondea: 999 se almacena como 99. El valor original permanece en el payload del caso.

Cuando risk_level está ausente o vacío, se deriva del score sin procesar:

Score (de 0 a 1000)risk_level derivado
950 o máscritical
de 850 a 949high
de 700 a 849medium
menos de 700low

Cuando no se envía ni risk_level ni score, el caso se crea con risk_level = medium.

La derivación solo se ejecuta cuando el campo está ausente o vacío. Solo se reconocen los cuatro valores anteriores, y un valor no vacío que no se reconoce omite la derivación y deja el caso sin ningún nivel de riesgo, por lo que envíe uno de los cuatro valores u omita el campo por completo.

priority ordena el caso en la cola y alimenta el motor de distribución junto con el score. Es independiente del nivel de riesgo.

fraud_analysis es lo que renderiza el panel de análisis de fraude del analista. Se almacena textualmente y aporta la explicación del workflow (etapas, políticas, reglas y sus contribuciones), el detalle de la transacción con device y ubicación, el perfil del cliente con líneas base de comportamiento y transacciones recientes, las comparaciones de anomalías, y las líneas de tiempo del cliente y de la contraparte. Envíelo en cada alerta para que el panel refleje este caso.

{
"explanation": {
"workflow_id": "wf-pix-out",
"workflow_name": "pix_out_standard",
"total_score": 780,
"decision": "REVIEW",
"review_threshold": 600,
"block_threshold": 900,
"stages_executed": 3,
"stages_total": 3,
"early_terminated": false,
"execution_time_ms": 45,
"stages_results": [
{
"stage_id": "st-velocity",
"stage_name": "velocity",
"score": 320,
"should_block": false,
"policies_results": [
{
"policy_id": "pol-velocity-amount",
"policy_name": "amount_vs_baseline",
"contribution": 200,
"score_weight": 0.4,
"rules_results": [
{
"rule_id": "rule-amount-4x",
"rule_name": "amount_over_4x_avg",
"passed": false,
"score": 300,
"score_contribution": 150,
"category": "velocity"
}
]
}
]
}
]
},
"transaction": {
"id": "tx-9f1c",
"amount": 35407.28,
"currency": "BRL",
"type": "TED",
"channel": "internet_banking",
"direction": "OUT",
"account_id": "acc-1234",
"counterpart_document": "04523967199",
"counterpart_name": "Empresa Suspeita LTDA",
"created_at": "2026-04-02T14:32:10Z",
"device": { "type": "mobile", "os": "Android 14", "is_new": false, "is_vpn": false, "is_proxy": false },
"location": { "city": "São Paulo", "state": "SP", "country": "BR", "ip": "189.40.72.33" }
},
"customer": {
"profile": {
"id": "cust-7788",
"name": "Ana Sophia Lopes",
"document_type": "CPF",
"document_number": "61589720458",
"customer_type": "person",
"customer_since": "2019-03-15",
"score": 100,
"kyc_status": "approved",
"risk_rating": "low",
"is_pep": false,
"sanctions_found": false,
"previous_fraud_count": 0
},
"baselines": [
{
"period_days": 90,
"transaction_type": "ALL",
"amount_avg": 7523.45,
"amount_stddev": 10724.0,
"amount_p95": 18000.0,
"tx_count": 23,
"unique_counterparts": 7,
"typical_hours": [9, 10, 11, 14, 15, 16, 17],
"typical_days": [1, 2, 3, 4, 5]
}
],
"recent_transactions": []
},
"comparisons": [
{ "label": "Valor", "is_anomalous": true, "severity": "high", "value": "4.7x", "detail": "R$ 35.407,28 vs média R$ 7.523,45" }
],
"network_stats": { "unique_payers": 8, "transaction_count": 12, "active_days": 45, "avg_transaction": 22500.0 },
"customer_timeline": { "account_id": "acc-1234", "transactions": [], "aggregates": [] },
"counterpart_timeline": { "counterpart_document": "04523967199", "transactions": [], "aggregates": [] }
}

Todos los subbloques son opcionales. Envíe lo que el motor tenga: un subbloque que no se envía simplemente no se renderiza, mientras que un subbloque cuyos tipos de campo no coinciden con el esquema se descarta, por lo que debe respetar la forma documentada.

Alerta mínima:

Ventana de terminal
curl -X POST https://{instance}.cmp.prod.guardline.com.br/api/v1/ingest/fraude \
-H "Content-Type: application/json" \
-H "X-API-Key: cmp_..." \
-d '{
"external_id": "fpp-case-001",
"subject_document": "12345678901",
"transaction_amount": 15000.50
}'

Alerta completa:

{
"external_id": "fpp-case-002",
"subject_name": "Ana Sophia Lopes",
"subject_document": "61589720458",
"counterparty_name": "Empresa Suspeita LTDA",
"transaction_type": "ted",
"transaction_amount": 35407.28,
"risk_level": "high",
"priority": 2,
"score": 810,
"sla_deadline": "2026-04-03T14:32:10Z",
"fraud_analysis": { "explanation": { "...": "..." }, "transaction": { "...": "..." }, "customer": { "...": "..." } }
}

201 Created, caso nuevo:

{
"case_id": "2ca0bd66-0ef9-44e6-9b47-6816264dd779",
"external_id": "fpp-case-002",
"created": true
}

200 OK, el external_id ya existe en la mesa Fraude:

{
"case_id": "2ca0bd66-0ef9-44e6-9b47-6816264dd779",
"external_id": "fpp-case-002",
"created": false
}

El caso nuevo entra en la mesa Fraude con estado new, estado de negocio aguardando_analise y un ítem de trabajo encolado. El motor de distribución lo asigna a un analista disponible de la cola de entrada.

EstadomessageCausa
400empty bodyNo se envió ningún cuerpo
400invalid jsonEl cuerpo no es JSON válido
400external_id é obrigatórioFalta el identificador
400subject_document é obrigatórioFalta el documento del cliente
400transaction_amount deve ser maior que zeroImporte ausente, no numérico o no positivo
401missing api key, invalid api keyVer Tokens de API
403token scope not authorized for this endpointEl alcance del token no es ingest:fraude ni ingest
429ingest rate limit exceededLímite por token superado
POST /api/v1/ingest/med

Alcance: ingest:med. Abre un caso en la mesa MED a partir de una solicitud del mecanismo especial de devolución (Mecanismo Especial de Devolução) recibida por la institución.

La mesa MED fue la primera mesa de la plataforma y acepta el conjunto más amplio de alias de claves, de modo que las exportaciones existentes del backoffice de PIX puedan reenviarse con una transformación mínima. El cuerpo completo se conserva como payload del caso.

CampoTipoObligatorioAliasDescripción
valor_solicitadonumbersíamount, valorImporte solicitado para la devolución, en BRL, mayor que cero. Se almacena en centavos
idstring o numberrecomendadoevent_id, notification_id, med_idIdentificador de la solicitud MED en el sistema de origen. Clave de idempotencia. Cuando todos están ausentes se usa el SHA-256 del cuerpo sin procesar, almacenado y devuelto como sha256: seguido del hash, por lo que un reenvío idéntico byte a byte también se deduplica. El hash del cuerpo no sobrevive a ningún cambio en la serialización (orden de las claves, formato de los números, una marca de tiempo incluida en el payload), por lo que no reemplaza el envío de un identificador real
request_idstringnomed_request_id, idNúmero de protocolo MED mostrado al analista. Cuando está ausente, se recurre a un valor derivado de una copia reserializada del payload, que no se garantiza que coincida con el external_id devuelto a quien realiza la llamada, por lo que conviene enviarlo de forma explícita
requested_atstring (RFC 3339)nocreated_at, event_atMomento en que se abrió la solicitud. Por defecto, el momento de la ingesta
motivostringnoreason_code, reason, tipogolpe (por defecto), fraude o erro_operacional. Los valores desconocidos se resuelven como golpe
statusstringnomed_statusaberta (por defecto), em_analise, aguardando_instituicao, aprovado, negado o recusado. Los valores desconocidos se resuelven como aberta
prazo_limite_atstring (RFC 3339)nodeadline_atPlazo regulatorio de respuesta de la solicitud MED
recebedoraobjectnorecipient, counterpartyInstitución receptora: { "nome": "...", "ispb": "..." }. Dentro de recebedora solo se lee nome; name funciona dentro de recipient y counterparty. También se aceptan las claves planas recebedora_nome, counterparty_name y recebedora_ispb. Cuando no se puede resolver ningún nombre de institución receptora, el caso se almacena con un nombre de marcador de posición
recebedora_respondeubooleannoIndica si la institución receptora ya respondió
valor_devolvidonumbernoImporte ya devuelto, en BRL
data_devolucao_atstring (RFC 3339)noMomento en que ocurrió la devolución
subject_namestringnocliente_nome, nomeNombre del cliente
subject_documentstringnocliente_documento, documentoCPF o CNPJ del cliente
transaction_typestringnotipo_transacaoPor defecto, pix
risk_levelstringnorisklow, medium, high o critical. Los valores desconocidos se ignoran
scoreintegernoDe 0 a 100. A diferencia de la mesa Fraude, el score de MED no se normaliza: ya debe estar en la escala de 0 a 100, y los valores fuera de ese rango se rechazan
priorityintegernoDe 0 a 9, cuanto menor más urgente. Por defecto, 3
Ventana de terminal
curl -X POST https://{instance}.cmp.prod.guardline.com.br/api/v1/ingest/med \
-H "Content-Type: application/json" \
-H "X-API-Key: cmp_..." \
-d '{
"id": "MED-2026-000123",
"requested_at": "2026-08-20T13:05:00Z",
"valor_solicitado": 4200.00,
"motivo": "golpe",
"status": "aberta",
"prazo_limite_at": "2026-08-27T13:05:00Z",
"subject_name": "Carlos Eduardo Nunes",
"subject_document": "47055107806",
"recebedora": { "nome": "Banco Exemplo S.A.", "ispb": "12345678" },
"priority": 2
}'

201 Created cuando el caso es nuevo, 200 OK con created: false cuando el identificador ya existe en la mesa MED:

{
"case_id": "8d1b3f4e-2a6c-4d7e-9b0a-1c2d3e4f5a6b",
"external_id": "MED-2026-000123",
"created": true
}
EstadomessageCausa
400empty body, invalid jsonSolicitud mal formada
400missing or invalid valor_solicitadoImporte ausente, no numérico o no mayor que cero
401missing api key, invalid api keyVer Tokens de API
403token scope not authorized for this endpointEl alcance del token no es ingest:med ni ingest
429ingest rate limit exceededLímite por token superado
POST /api/v1/ingest/pld

Alcance: ingest:pld. Abre o enriquece un caso en la mesa AML (prevenção à lavagem de dinheiro) a partir de una alerta transaccional producida por un motor de monitoreo. La mesa AML se diferencia de las demás en dos aspectos: las alertas de un cliente que ya tiene un caso abierto se consolidan en ese caso en lugar de abrir uno nuevo, y los movimientos en efectivo iguales o superiores al umbral regulatorio se marcan como una COE automática (comunicação de operação em espécie) que omite el triaje.

CampoTipoObligatorioAliasDescripción
alerta_externo_idstringsíIdentificador único de la alerta en el sistema de origen. Clave de idempotencia. Está limitado a 100 caracteres; actualmente un valor más largo falla como error de servidor en lugar de como error de validación, por lo que conviene respetar ese límite
cpf_cnpjstringsíCPF (11 dígitos) o CNPJ (14 dígitos) del cliente investigado. La puntuación se elimina antes de la validación
tipo_alertastringsíCódigo del tipo de alerta, sin distinción de mayúsculas y minúsculas. Ver Tipos de alerta
subject_namestringrecomendadocliente_nome, nomeNombre del cliente
score_risconumberrecomendadoscoreScore de riesgo. Se aceptan tanto la escala de 0 a 100 como la de 0 a 1000; los valores superiores a 100 se dividen entre 10
valor_totalnumberrecomendadovalorImporte total del movimiento, en BRL. Lo usa la regla de COE
valor_total_centavosintegernovalor_centavosEl mismo importe en centavos. Tiene precedencia sobre valor_total cuando ambos están presentes
tipologia_sugeridastring[]noCódigos de tipología sugeridos por el motor, por ejemplo ["PIX001", "EST001"]
dados_clienteobjectnoContexto libre del cliente (ingresos declarados, actividad, ciudad, estado). Se almacena textualmente y se muestra en el expediente
dados_transacionaisobjectnoContexto transaccional libre (cantidad de operaciones, período, canales). Se almacena textualmente
contrapartesarraynoContrapartes involucradas. Se almacenan textualmente
is_coebooleannoFuerza la clasificación como COE automática sin importar el tipo ni el importe
priorityintegernoDe 0 a 9, cuanto menor más urgente. Por defecto, 3. Una COE automática siempre queda en prioridad 1
fpp_source_urlstringnoEnlace directo a la alerta en el sistema de origen, mostrado en el expediente

tipo_alerta debe ser uno de los códigos siguientes. Un código desconocido se rechaza con 404.

CódigoDescripción
pixTransferencia PIX
tedTransferencia TED
docTransferencia DOC
boletoPago de boleto
especieMovimiento en efectivo (billetes). El único tipo elegible para la regla de COE automática
cambioOperación de cambio de divisas
cartaoOperación con tarjeta
depositoDepósito
saqueRetiro
transferenciaTransferencia genérica entre cuentas
outroCualquier otro movimiento

Cada alerta pasa por cuatro pasos dentro de una única transacción:

  1. Idempotencia. Si ya existe una alerta con el mismo alerta_externo_id, la API responde 200 con is_new_case: false y consolidated_into apuntando al número del caso que contiene la alerta original. No se escribe ningún dato de caso ni de alerta; el reenvío queda registrado en la traza de auditoría. Dos llamadas con el mismo identificador que llegan en el mismo instante son una situación distinta: una de ellas pierde la carrera contra el índice único y recibe 409 con el código ALREADY_EXISTS, que es seguro tratar como un duplicado.
  2. Detección de COE. La alerta es una COE automática cuando is_coe es true, o cuando tipo_alerta es especie y el importe total es igual o superior a R$ 50.000,00. Una COE automática siempre crea un caso nuevo (nunca se consolida), comienza en el estado AML pendente_geracao_xml con prioridad 1 y se proyecta como decided / aprovado en el estado genérico del caso, de modo que aparece como lista para el paso de comunicación al COAF.
  3. Consolidación. Cuando la alerta no es una COE y el mismo cpf_cnpj ya tiene un caso AML activo (cualquier estado AML distinto de arquivado_triagem, arquivado, comunicado_coaf, retificado o cancelado), la alerta se adjunta a ese caso. La API responde 200 con is_new_case: false y consolidated_into apuntando al número del caso existente. El analista ve la nueva alerta en el expediente del caso que ya está trabajando. Una alerta COE siempre abre su propio caso y nunca se consolida en un caso activo existente. La COE también exige que el sistema de origen marque la alerta como una operación en efectivo (tipo_alerta igual a especie, o el indicador explícito is_coe): el umbral de importe por sí solo no la activa.
  4. Caso nuevo. En caso contrario se crea un caso AML nuevo con estado new, estado de negocio aguardando_analise, estado AML novo, un plazo de selección de 45 días (prazo_selecao) y un número de caso legible con la forma PLD-YYYY-NNNNN. La API responde 201.

Alerta mínima:

{
"alerta_externo_id": "ALR-2026-000981",
"cpf_cnpj": "12345678901",
"tipo_alerta": "pix"
}

Alerta recomendada:

Ventana de terminal
curl -X POST https://{instance}.cmp.prod.guardline.com.br/api/v1/ingest/pld \
-H "Content-Type: application/json" \
-H "X-API-Key: cmp_..." \
-d '{
"alerta_externo_id": "ALR-2026-000982",
"cpf_cnpj": "321.987.654-00",
"tipo_alerta": "pix",
"subject_name": "Marcia Helena Souza",
"score_risco": 82,
"valor_total": 47500,
"tipologia_sugerida": ["PIX001"],
"dados_cliente": {
"cidade": "São Paulo",
"uf": "SP",
"renda_declarada": 4500,
"atividade": "Vendedora autônoma"
},
"dados_transacionais": {
"qtd_operacoes": 18,
"periodo_dias": 5
},
"fpp_source_url": "https://fpp.example.com/alerts/ALR-2026-000982"
}'

Movimiento en efectivo que dispara una COE automática:

{
"alerta_externo_id": "ALR-2026-000990",
"cpf_cnpj": "12345678000199",
"tipo_alerta": "especie",
"subject_name": "Acme Serviços LTDA",
"valor_total_centavos": 7500000
}

201 Created, caso nuevo:

{
"case_id": "2ca0bd66-0ef9-44e6-9b47-6816264dd779",
"case_pld_detail_id": "21be5573-402d-49d8-ab8d-461cb27f60b7",
"numero_caso": "PLD-2026-00021",
"is_new_case": true,
"is_coe_automatica": false,
"alerta_externo_id": "ALR-2026-000982"
}

200 OK, alerta duplicada o alerta consolidada en un caso activo del mismo cliente:

{
"case_id": "5378d55e-f23f-4fd8-a2ce-1a28e9399535",
"case_pld_detail_id": "b039a7cc-2a32-4dec-a6f2-5b7d643c82d3",
"numero_caso": "PLD-2026-00006",
"is_new_case": false,
"is_coe_automatica": false,
"consolidated_into": "PLD-2026-00006",
"alerta_externo_id": "ALR-2026-000983"
}
CampoDescripción
case_idUUID del caso que contiene la alerta
case_pld_detail_idUUID del registro de detalle AML de ese caso
numero_casoNúmero de caso legible, el identificador que citan los operadores
is_new_casetrue cuando esta llamada creó un caso
is_coe_automaticatrue cuando el caso es una COE automática. Solo se completa cuando esta llamada crea o consolida el caso: una repetición idempotente simple devuelve false incluso para un caso COE, por lo que debe leerse de la respuesta de creación y no de una repetición
consolidated_intoPresente solo cuando is_new_case es false: el número del caso que absorbió la alerta, ya sea porque el alerta_externo_id ya era conocido o porque el cliente tenía un caso activo
alerta_externo_idEco del identificador de la solicitud

Para distinguir las dos situaciones de 200, compare numero_caso con lo que esperaba el sistema de origen: una duplicación devuelve el caso de la alerta original, una consolidación devuelve un caso que fue abierto por una alerta distinta.

EstadomessageCausa
400empty body, invalid jsonSolicitud mal formada
400alerta_externo_id is requiredFalta el identificador
400cpf_cnpj must be a valid 11 or 14-digit documentTras eliminar los caracteres no numéricos, el documento no tiene 11 ni 14 caracteres
400tipo_alerta is requiredFalta el tipo de alerta
404pld_alert_type "xyz" not foundtipo_alerta no es uno de los tipos de alerta; el mensaje cita el valor que fue enviado
401missing api key, invalid api keyVer Tokens de API
403token scope not authorized for this endpointEl alcance del token no es ingest:pld ni ingest
429ingest rate limit exceededLímite por token superado
POST /api/v1/ingest/kyc

Alcance: ingest:kyc. Abre un caso en las mesas de onboarding: Cadastro PF (KYC, personas físicas) o Cadastro PJ (KYB, personas jurídicas). No existe una ruta /ingest/kyb separada; la mesa se elige según el flow_type del payload.

El endpoint acepta dos formas de cuerpo, discriminadas por el campo event_type:

FormaCuándoDiscriminador
Sobre de onboardingEl caso proviene del ONP de Guardline, o de un motor de onboarding que sigue el mismo contrato. Admite los flujos KYC, KYC de menores, representante legal y KYB, y devuelve las decisiones terminales para su conciliaciónevent_type es cmp.case.review, cmp.case.approved o cmp.case.rejected
Payload KYC genéricoUn motor de decisión de terceros envía su análisis KYC completo de una persona físicaevent_type ausente o con cualquier otro valor

Ambas formas responden con el mismo cuerpo { case_id, external_id, created }.

Además del token de API, el endpoint puede exigir una firma HMAC. La capa de firma se habilita por instancia, cuando se aprovisiona un secreto compartido durante la configuración de la integración; hasta entonces solo se exige X-API-Key. Cuando está habilitada, la firma se verifica antes que el token de API.

EncabezadoValor
X-Guardline-TimestampTiempo Unix en segundos (entero) en el momento de la firma
X-Guardline-SignatureHMAC-SHA256 de {timestamp}.{body} en hexadecimal minúsculo

Procedimiento de firma:

  1. Serialice el cuerpo una sola vez. Los bytes exactos enviados deben ser los bytes firmados; no vuelva a serializar ni aplique formato legible después de firmar.
  2. Concatene el timestamp, un punto literal y el cuerpo sin procesar: {timestamp}.{body}.
  3. Calcule el HMAC-SHA256 de esa cadena con el secreto compartido y codifique el digest en hexadecimal.
Ventana de terminal
BODY='{"event_type":"cmp.case.review","execution_id":"...","flow_type":"kyc", "...":"..."}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)
curl -X POST https://{instance}.cmp.prod.guardline.com.br/api/v1/ingest/kyc \
-H "Content-Type: application/json" \
-H "X-API-Key: $TOKEN" \
-H "X-Guardline-Timestamp: $TS" \
-H "X-Guardline-Signature: $SIG" \
--data-raw "$BODY"

El timestamp debe estar dentro de los 5 minutos del reloj del servidor (la tolerancia es configurable por instancia). Los fallos de firma responden 401 con uno de estos mensajes: missing signature, missing timestamp, malformed timestamp, stale timestamp, malformed signature o invalid signature.

Este es el mismo formato que usa el ONP de Guardline para sus webhooks salientes, por lo que un integrador que ya verifica los webhooks del ONP puede reutilizar la rutina para firmar las llamadas al CMP.

El sobre es el cuerpo que el ONP de Guardline envía al CMP cuando una ejecución de onboarding se deriva a análisis manual, y de nuevo cuando la ejecución alcanza una decisión terminal. La referencia de la API del ONP describe el lado productor; esta sección describe lo que el CMP espera y hace.

event_typeEfecto
cmp.case.reviewCrea el caso y lo encola en la mesa. Este es el único evento que crea un caso. Un reenvío para el mismo execution_id responde 200 con created: false
cmp.case.approvedConcilia una aprobación terminal sobre un caso creado previamente por un review. Si el caso todavía no tiene decisión, la aprobación se registra como la decisión. Nunca crea un caso
cmp.case.rejectedIgual que el anterior, para un rechazo. El código de decisión kyc_reject_* (o kyb_reject_*) se elige a partir de rejection_reasons

Un evento terminal para un execution_id que nunca produjo un review responde 200 con un case_id vacío y created: false: la ejecución fue decidida automáticamente por el motor, nunca llegó a la mesa, y el CMP deliberadamente no fabrica un caso encolado para ella. Un evento terminal nunca sobrescribe una decisión que un analista ya registró.

CampoTipoObligatorioDescripción
event_typestringsíVer Semántica de los eventos
execution_idstringsíIdentificador de la ejecución de onboarding. Se almacena como el external_id del caso y se usa como clave de idempotencia
flow_typestringsíkyc (persona física adulta), kyc_minor (menor con un representante legal), kyc_representative (la ejecución propia del representante, revisada como caso independiente) o kyb (empresa). kyb enruta a la mesa Cadastro PJ, los demás a Cadastro PF
subjectobjectsíVer abajo
case_envelopeobjectsíVer abajo
event_idstringnoIdentificador del evento del lado productor, almacenado junto con el caso
fired_atstring (RFC 3339)noMomento en que el productor emitió el evento
tenant_idstringnoTenant del productor, informativo
flow_id, flow_group_codestringnoFlujo y grupo de flujo de la ejecución, informativos
edd_flaggedbooleannoLa ejecución requiere diligencia debida reforzada. Se muestra al analista
rejection_reasonsstring[]noMotivos en texto libre detrás de un rechazo. Se usan para clasificar el código de decisión del rechazo
derivation_reasonsobject[]noPor qué el motor derivó el caso a análisis manual: { "code", "label_ptbr", "severity", "source", "detail" }. Se renderiza textualmente en el expediente
representativeobjectnoSolo kyc_minor: { "execution_id", "status" } de la ejecución del representante legal
guardian_typestringnoSolo kyc_minor: parent, mother, father u other
representativesobject[]noSolo kyb: los representantes legales de la empresa, { "execution_id", "status", "name", "flag", "document", "document_type" }. flag es true cuando el representante fue rechazado o expiró. Las entradas que no tienen a la vez execution_id y name se descartan

subject:

CampoTipoObligatorioDescripción
documentstringsolo en eventos terminalesCPF o CNPJ, solo dígitos (la puntuación se elimina). Un cmp.case.review puede llegar con el documento vacío cuando la ejecución fue derivada antes de completarse el paso de identidad; el analista resuelve la identidad en la mesa
document_typestringnocpf o cnpj
namestringnoNombre del sujeto, mostrado en la cola
email, phone, birth_datestringnoSe capturan en el payload del caso

case_envelope:

CampoTipoObligatorioDescripción
prioritystringsíP0, P1, P2 o P3, cuanto menor más urgente. Se mapea a la prioridad del caso de 0 a 3. Cualquier otro valor se rechaza
case_statusstringnoEstado del motor: approved, rejected, blocked, escalated u otro. Determina la decisión de workflow que se muestra al analista (ver el mapeo abajo)
decision_codestringnoDecisión del motor: approved, rejected o pending_review (también se aceptan las formas cortas approve, reject y review). Determina la decisión terminal del CMP en cmp.case.approved / cmp.case.rejected
risk_levelstringnolow, medium, high o critical, sin distinción de mayúsculas y minúsculas. Los valores desconocidos se ignoran
total_scorenumbernoScore del motor. Se almacena como score del workflow y, limitado al rango de 0 a 100, como el score del caso que usan las colas
sla_deadline_at, completed_at, expires_atstring (RFC 3339)noMarcas de tiempo del productor. completed_at se convierte en el momento de la decisión en un evento terminal
OrigenAtributo del casoRegla
case_envelope.case_statusDecisión de workflow mostrada en la mesaapproved pasa a ALLOW; rejected y blocked pasan a BLOCK; cualquier otro valor pasa a REVIEW
case_envelope.decision_code + rejection_reasonsCódigo de decisión terminalapproved registra kyc_approve_onboarding (kyb_approve_onboarding para empresas). rejected registra kyc_reject_fraud_suspicion cuando los motivos mencionan fraud, suspicious o scam; kyc_reject_document_invalid cuando mencionan document, invalid_doc, expired o illegible; kyc_reject_compliance en los demás casos. pending_review no registra nada: decide el analista
case_envelope.priorityPrioridad del casoDe P0 a P3 se mapean de 0 a 3
case_envelope.risk_levelNivel de riesgo del casoSe pasa a minúsculas; los valores desconocidos dejan el nivel de riesgo sin definir
case_envelope.total_scoreScore del casoSe redondea y se limita al rango de 0 a 100
subject.name, subject.documentColumnas de la colaSe muestran en el listado de la mesa

Los casos creados por un cmp.case.review entran en la mesa con estado new, estado de negocio aguardando_analise y un ítem de trabajo encolado, exactamente igual que en las demás mesas. Solo el sobre se persiste en el momento de la ingesta; el expediente completo (documentos, biometría, resultados de proveedores, árbol de decisión) se obtiene del ONP bajo demanda cuando el analista abre el caso.

Caso KYC derivado a análisis manual:

{
"event_id": "01HZX5K7TPMJ9R2QXYZABCDEF1",
"event_type": "cmp.case.review",
"fired_at": "2026-05-25T17:00:00Z",
"tenant_id": "default",
"execution_id": "7b5c3d22-3e5f-4a7b-93b8-c6f5a2d4a4e2",
"flow_id": "61000000-0000-0000-0000-000000000003",
"flow_type": "kyc_minor",
"flow_group_code": "Guardline KYC Menor",
"case_envelope": {
"case_status": "escalated",
"decision_code": "pending_review",
"risk_level": "medium",
"total_score": 480,
"priority": "P1",
"sla_deadline_at": "2026-05-26T17:00:00Z",
"completed_at": "2026-05-25T17:00:00Z",
"expires_at": "2026-06-25T17:00:00Z"
},
"subject": {
"document": "11122233344",
"document_type": "cpf",
"name": "Pedro Henrique Silva",
"email": "pedrinho@example.com",
"phone": "+5511977665544",
"birth_date": "2010-07-08"
},
"representative": {
"execution_id": "6c4d2e11-4a6b-5c8d-04c9-d7e6b3f5b5a3",
"status": "completed"
},
"rejection_reasons": [],
"derivation_reasons": [
{ "code": "biometrics_not_completed", "label_ptbr": "Biometria não concluída", "severity": "medium", "source": "onp" }
],
"edd_flagged": false,
"guardian_type": "mother"
}

Caso KYB derivado a análisis manual:

{
"event_id": "6f7d1e10-2a3b-4c5d-8e9f-0a1b2c3d4e5f",
"event_type": "cmp.case.review",
"fired_at": "2026-07-17T17:00:00Z",
"tenant_id": "default",
"execution_id": "9a1f6c33-5d2e-4b8a-a7c1-2e3f4a5b6c7d",
"flow_type": "kyb",
"flow_group_code": "Guardline KYB",
"case_envelope": {
"case_status": "escalated",
"decision_code": "pending_review",
"risk_level": "high",
"total_score": 720,
"priority": "P1",
"sla_deadline_at": "2026-07-18T17:00:00Z",
"completed_at": "2026-07-17T17:00:00Z"
},
"subject": {
"document": "12345678000199",
"document_type": "cnpj",
"name": "Acme Serviços Financeiros LTDA"
},
"representatives": [
{ "execution_id": "0c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f", "status": "completed", "name": "Maria Oliveira", "flag": false, "document": "47055107806", "document_type": "cpf" },
{ "execution_id": "1d2e3f4a-5b6c-7d8e-9f0a-1b2c3d4e5f6a", "status": "expired", "name": "João Pereira", "flag": true, "document": "61589720458", "document_type": "cpf" }
],
"rejection_reasons": [],
"derivation_reasons": [
{ "code": "provider_unavailable", "label_ptbr": "Consulta de QSA indisponível", "severity": "medium", "source": "kyb_provider" }
],
"edd_flagged": true
}

Rechazo terminal conciliado sobre el caso anterior:

{
"event_type": "cmp.case.rejected",
"execution_id": "9a1f6c33-5d2e-4b8a-a7c1-2e3f4a5b6c7d",
"flow_type": "kyb",
"subject": { "document": "12345678000199", "document_type": "cnpj" },
"case_envelope": {
"case_status": "rejected",
"decision_code": "rejected",
"priority": "P1",
"completed_at": "2026-07-18T09:12:00Z"
},
"rejection_reasons": ["document_expired"]
}

Un motor de onboarding o de decisión de terceros que no implementa el sobre puede enviar su análisis KYC completo de una persona física. Tres campos son obligatorios; todo lo demás es opcional y, cuando está presente, se indexa para que la mesa pueda filtrarlo y mostrarlo. El cuerpo completo se conserva como payload del caso. Las claves se aceptan en camelCase (tal como se listan) y, en la mayoría de ellas, también en snake_case.

CampoTipoDescripción
case_idstringIdentificador único del análisis en el sistema de origen. Clave de idempotencia. Se aceptan external_id e id como alias
subject.personal.cpfstringCPF del cliente, 11 dígitos sin puntuación. También se lee de subject.basicData.cpf, subject.cpf.cpf o de un subject_document en el nivel raíz
decision.finalDecisionstringALLOW, REVIEW o BLOCK, sin distinción de mayúsculas y minúsculas. Se acepta decision.final_decision
BloqueCampos que lee el CMP
subject.personalname (también subject.basicData.name, subject.cpf.name, subject_name en el nivel raíz)
decisionfinalScore (de 0 a 100), thresholds.review, thresholds.block, workflowExplanation.{workflowId, workflowName, totalScore, reviewThreshold, blockThreshold, executionTimeMs, executedAt, stagesResults[]}
trustriskScore, riskLabel
facematchmatched, confidenceScore
livenessmode
facialriskScore, riskLevel
documentoscopiaqualityScore, confidenceScore, isExpired
complianceisPep, sanctions[], lawsuits[], govDebts[], mediaExposureUrls[]
contactmediaExposureUrls[]
deviceriskScore, isVpn, isEmulator, isRooted
financial.financialRiskrisk_level, financial_risk_score, is_on_collection
payload_versionEtiqueta de versión del contrato, v1 por defecto

El CMP deriva la prioridad y el nivel de riesgo del caso a partir de la decisión y de las señales de compliance, biometría y device:

AtributoRegla
Prioridad (cuanto menor, más urgente)Valor base según la decisión: BLOCK 1, REVIEW 3, ALLOW 5. Cualquier coincidencia en listas de sanciones fuerza 0. Un PEP eleva a 1 como máximo. Procesos judiciales, deudas con organismos públicos, facial.riskLevel = high, o un device marcado como VPN, emulador o con root, elevan a 2 como máximo
Nivel de riesgoBLOCK es critical. REVIEW es high cuando el score final alcanza al menos el 90% del umbral de bloqueo; en caso contrario, medium. ALLOW es low. Las sanciones o un PEP elevan low y medium a high
Scoredecision.finalScore (o workflowExplanation.totalScore), limitado al rango de 0 a 100
{
"case_id": "kyc-2026-000451",
"subject": {
"personal": { "cpf": "11122233344", "name": "Roberto Alves Lima" }
},
"decision": {
"finalDecision": "REVIEW",
"finalScore": 65,
"thresholds": { "review": 50, "block": 80 },
"workflowExplanation": {
"workflowId": "wf-kyc-standard",
"workflowName": "kyc_standard",
"totalScore": 65,
"reviewThreshold": 50,
"blockThreshold": 80,
"executionTimeMs": 1830,
"executedAt": "2026-08-20T10:15:00Z"
}
},
"compliance": {
"isPep": true,
"sanctions": [],
"lawsuits": [{ "court": "TJSP", "number": "1001234-56.2024.8.26.0100", "type": "civil" }],
"govDebts": []
},
"facematch": { "matched": true, "confidenceScore": 88.5 },
"liveness": { "mode": "active" },
"facial": { "riskScore": 20, "riskLevel": "low" },
"documentoscopia": { "qualityScore": 92, "confidenceScore": 95, "isExpired": false },
"device": { "riskScore": 10, "isVpn": false, "isEmulator": false, "isRooted": false },
"financial": { "financialRisk": { "risk_level": "medium", "financial_risk_score": 55, "is_on_collection": false } }
}

Este payload llega a la mesa Cadastro PF con prioridad 1 (PEP) y nivel de riesgo high (un PEP eleva medium a high).

201 Created cuando el caso es nuevo, 200 OK con created: false en un reenvío o en una conciliación terminal:

{
"case_id": "9e1c8b3a-1234-5678-9abc-def012345678",
"external_id": "7b5c3d22-3e5f-4a7b-93b8-c6f5a2d4a4e2",
"created": true
}
EstadomessageCausa
400empty body, invalid jsonSolicitud mal formada
400execution_id e obrigatorioSobre sin execution_id
400flow_type "x" invalido (esperado kyc, kyb, kyc_minor ou kyc_representative)flow_type desconocido
400subject e obrigatorio, case_envelope e obrigatorioFaltan objetos del sobre
400subject.document e obrigatorioEvento terminal sin documento
400case_envelope.priority "x" invalida (esperado P0, P1, P2 ou P3)Prioridad fuera del conjunto aceptado
400case_id e obrigatorioPayload genérico sin identificador
400subject.personal.cpf e obrigatorioPayload genérico sin CPF
400decision.finalDecision deve ser ALLOW, REVIEW ou BLOCKPayload genérico con una decisión inválida
401missing signature, invalid signature, stale timestamp y relacionadosCapa HMAC habilitada y firma fallida
401missing api key, invalid api keyVer Tokens de API
403token scope not authorized for this endpointEl alcance del token no es ingest:kyc ni ingest
429ingest rate limit exceededLímite por token superado

Los casos viven en mesas (operaciones). Cada mesa tiene una cola de entrada donde llegan los casos nuevos y una cola de supervisión para las escalaciones. El código de la mesa es el identificador estable que usa la API, por ejemplo en la solicitud de transferencia entre mesas; los nombres visibles de las colas se eligen en el momento de la implantación y un administrador puede renombrarlos.

Código de mesaMesaTipo de casoEndpoint de ingesta
medMEDmed/api/v1/ingest/med
fraudeFraudefraude/api/v1/ingest/fraude
kycCadastro PFkyc/api/v1/ingest/kyc
kybCadastro PJkyb/api/v1/ingest/kyc
pldAMLpld/api/v1/ingest/pld
comercialComercialconserva el tipo de la mesa de origenninguno, recibe casos por transferencia

Cada caso lleva un estado genérico, compartido por todas las mesas, y un estado de negocio que lo refina:

Estado genéricoSignificado
newCreado por la ingesta, a la espera de distribución o de ser tomado
in_analysisAsignado a un analista
escalatedEnviado a la cola de supervisión
decidedSe registró una decisión
closedCerrado
failedLa ocurrencia no pudo abrirse ni procesarse; el caso se libera para su reprocesamiento

La mesa AML añade su propio workflow por encima (novo, em_triagem, em_investigacao, aguardando_revisao_investigacao, aguardando_comite, pendente_geracao_xml, xml_gerado, comunicado_coaf, erro_siscoaf, retificado, cancelado, arquivado, entre otros). Sus transiciones son las que se publican a los receptores como webhooks salientes.

Los plazos de SLA se calculan en la ingesta a partir de la política de SLA de la mesa y del estado (configurable por mesa, cola y estado, con un umbral de advertencia antes del vencimiento). La mesa Fraude también acepta un sla_deadline explícito en el payload.

Un analista puede entregar un caso a otra mesa cuando el análisis revela una naturaleza distinta (sospecha de fraude en un caso de onboarding, indicios de lavado de dinero, necesidad de una opinión comercial). El caso conserva su tipo y su historial, y vuelve por el mismo camino. Pares habilitados hoy:

DesdeHacia
Cadastro PF (kyc)Fraude, AML, Comercial (y de vuelta)
Cadastro PJ (kyb)Fraude, AML, Comercial (y de vuelta)

Las transferencias se registran en la traza de auditoría con origen, destino, actor y justificación. No son visibles para el sistema de origen a través de la API; la decisión que finalmente cierra el caso sí lo es.

Cómo se entera el sistema de origen de la decisión del analista depende de la mesa.

Para los casos abiertos mediante el sobre de onboarding, el CMP envía la decisión del analista (aprobar, rechazar) de vuelta al ONP de Guardline en cuanto queda registrada, haciendo referencia al execution_id. Solo se propagan esas decisiones terminales: las decisiones intermedias, como un escalamiento o una solicitud de información adicional, permanecen en el CMP y no producen ningún evento del ONP. El ONP retoma entonces la ejecución y emite sus propios eventos de onboarding hacia el integrador. Por lo tanto, un integrador que usa ONP y CMP juntos consume un único flujo de webhooks, el descrito en la referencia de la API del ONP, y no necesita hacer polling al CMP.

El retorno se entrega a través del mismo outbox durable que los webhooks salientes descritos abajo. Durante una caída temporal del ONP se reintenta unas pocas veces a lo largo de aproximadamente dos minutos y luego se marca como agotado; después de eso no hay ningún reprocesamiento automático, por lo que recuperar la notificación exige que el equipo de operaciones de Guardline la vuelva a encolar de forma manual.

La mesa AML publica sus hitos de workflow a los receptores HTTP configurados para la instancia. Un receptor se define por un nombre, una URL de destino, un secreto HMAC y la lista de eventos a los que se suscribe, y lo aprovisiona Guardline durante la configuración de la integración. Varios receptores pueden suscribirse al mismo evento.

EventoSe emite cuandopld_status resultante
triagem_concluidaLa alerta fue archivada durante el triaje, después de la investigación, o por el director al archivar un caso que el comité rechazó. En este tercer caso la entrega lleva action: "pld_arquivar_reprovado" y llega justo después de comite_reprovado, por lo que conviene enrutar según payload.action y no solo según el nombre del eventoarquivado_triagem o arquivado
comite_aprovadoLa sesión del comité cerró con la aprobación de la comunicaciónaprovado_comite
comite_reprovadoLa sesión del comité cerró rechazando la comunicaciónreprovado_comite
coaf_registradoLa comunicación al COAF fue registrada (protocolo SISCOAF recibido)comunicado_coaf
coaf_erroEl envío a SISCOAF falló y el error quedó registradoerro_siscoaf
coaf_retificadoLa comunicación fue rectificadaretificado
coaf_canceladoLa comunicación fue canceladacancelado

Los números de protocolo, los hashes y los comprobantes de la comunicación al COAF deliberadamente no viajan en el webhook. Están disponibles para los usuarios autorizados en la mesa.

Cada entrega envía el mismo sobre; el bloque payload es estable en todos los eventos AML, de modo que un único decodificador los maneja todos.

{
"event": "coaf_registrado",
"delivery_id": "3f0c9b1e-8d2a-4e6f-b5c7-1a2b3c4d5e6f",
"correlation_key": "PLD-2026-00042",
"case_id": "2ca0bd66-0ef9-44e6-9b47-6816264dd779",
"attempt": 1,
"timestamp": "2026-08-20T14:05:11Z",
"payload": {
"case_id": "2ca0bd66-0ef9-44e6-9b47-6816264dd779",
"numero_caso": "PLD-2026-00042",
"action": "pld_registrar_coaf",
"pld_status": "comunicado_coaf",
"occurred_at": "2026-08-20T14:05:10Z"
}
}
CampoDescripción
eventNombre del evento, según la tabla anterior
delivery_idIdentificador estable de esta entrega, idéntico en todos los reintentos. Úselo para deduplicar
correlation_keyEl número del caso AML
case_idUUID del caso
attempt1 en la primera entrega, 2 y 3 en los reintentos
timestampMomento en que se envió este intento
payload.actionLa acción de workflow que produjo la transición (por ejemplo pld_arquivar_triagem, pld_registrar_coaf, pld_retificar, comite_aprovado)
payload.pld_statusEl estado AML resultante
payload.motivoPresente solo en coaf_retificado y coaf_cancelado: la justificación introducida por el operador
payload.occurred_atMomento en que se confirmó la transición
EncabezadoDescripción
Content-TypeSiempre application/json
X-Webhook-EventEl nombre del evento, para enrutar sin analizar el cuerpo
X-Webhook-DeliveryEl identificador de la entrega, el mismo valor que delivery_id en el cuerpo
X-Webhook-SignatureHMAC-SHA256 del cuerpo sin procesar, en hexadecimal

La firma cubre únicamente el cuerpo sin procesar de la solicitud (no hay componente de timestamp, a diferencia de la firma entrante de onboarding).

  1. Lea los bytes del cuerpo sin procesar, antes de cualquier análisis JSON.
  2. Calcule el HMAC-SHA256 del cuerpo con el secreto del receptor y codifíquelo en hexadecimal.
  3. Compare con X-Webhook-Signature usando una comparación en tiempo constante.
import hmac, hashlib
def verify_cmp_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)

Una entrega es exitosa cuando el receptor responde con cualquier estado 2xx en menos de 10 segundos. En caso contrario, el CMP reintenta:

IntentoEspera tras el fallo anterior
2En el siguiente vaciado posterior al fallo
3Al menos 30 segundos después de eso

El outbox lo vacía un job que se ejecuta cada 60 segundos, por lo que el intervalo de vaciado domina los tiempos reales y toda la ventana de reintentos es de unos dos minutos. Tras el tercer fallo, la entrega se marca como exhausted y ya no se reintenta automáticamente. Las entregas se persisten en un outbox antes del primer intento, de modo que un reinicio del CMP nunca pierde un evento; la garantía rige una vez que el registro de la entrega queda escrito. Volver a encolar una entrega agotada es una operación manual que realiza el equipo de operaciones de Guardline a pedido, no una API de autoservicio.

RequisitoDetalle
ProtocoloSe exige HTTPS
Versión de TLS1.2 o superior
Respuesta de éxitoCualquier código 2xx, el cuerpo se ignora
Tiempo de esperaResponder en menos de 10 segundos
IdempotenciaDeduplicar por X-Webhook-Delivery (o delivery_id)

Reciba el webhook, persístalo o encólelo, y responda 200 de inmediato. Haga el procesamiento de forma asíncrona.

Las decisiones de las mesas Fraude y MED se registran en el CMP con su justificación, su código de decisión y sus efectos operativos (retención cautelar, bloqueo preventivo de la cuenta, inclusión en listas) y son visibles en el detalle del caso y en la traza de auditoría de la consola. El pipeline de webhooks salientes es común a todas las mesas, pero por ahora no se publica ningún evento para esas mesas; cuando la institución necesita que el resultado de fraude o MED se envíe a su sistema core, la suscripción se habilita como parte de la configuración de la integración.

Los tokens los gestionan los administradores del CMP. Los endpoints siguientes requieren la cookie de sesión de consola de un usuario con el rol admin, que el rol de director de AML hereda, por lo que un director de AML también puede gestionar tokens; no se pueden llamar con un token de API. Las mismas operaciones están disponibles en la consola.

GET /api/v1/api-tokens

Devuelve todos los tokens con su etiqueta, prefijo, alcance, indicador de actividad, límite de solicitudes, vencimiento, contador de uso y último uso. Los valores de los tokens nunca se devuelven.

POST /api/v1/api-tokens
{
"label": "fpp-producao",
"scope": "ingest:fraude",
"rate_limit_max": 300,
"rate_limit_window_seconds": 60,
"expires_in_days": 365
}
CampoObligatorioDescripción
labelsíNombre de visualización
scopenoUno de los alcances. Por defecto, ingest
rate_limit_maxnoSolicitudes por ventana. Por defecto, 60
rate_limit_window_secondsnoDuración de la ventana. Por defecto, 60
expires_in_daysnoDías hasta el vencimiento. 0 o ausente significa sin vencimiento

Respuesta 201:

{
"ok": true,
"id": "c9a1e845-a565-43b6-ac29-2d3072b027b9",
"token": "cmp_e188905ec47a80ec035fe31a3264bf1a5f9231f1353ea24717cf311740305f11",
"token_prefix": "cmp_e188905e",
"label": "fpp-producao",
"scope": "ingest:fraude"
}

El campo token aparece solo en esta respuesta. Guárdelo de inmediato en la bóveda de secretos del sistema de origen; no se puede recuperar después. Si se pierde, cree un token nuevo y rote.

Rotación sin interrupción: cree el token nuevo, configúrelo en el sistema de origen, confirme una ingesta exitosa y luego desactive el antiguo.

PATCH /api/v1/api-tokens/{id}/toggle
{ "is_active": false }

La desactivación conserva el registro del token y su historial de uso para auditoría. Un token desactivado responde 401 invalid api key y puede reactivarse con is_active: true.

DELETE /api/v1/api-tokens/{id}

La eliminación es permanente.

GET /api/v1/settings/webhooks

Devuelve el catálogo de todos los endpoints entrantes que expone la instancia (las cuatro rutas de ingesta más los callbacks de los proveedores de comunicación para WhatsApp, SMS y voz), con la ruta, los métodos aceptados, el tipo de autenticación y su encabezado, y el alcance de token requerido. El catálogo se deriva de la tabla de rutas de la versión en ejecución, por lo que es la forma más rápida de que un administrador confirme la URL exacta que debe entregar a un sistema de origen.

EstadoSignificado
200Solicitud procesada; el caso ya existía (repetición idempotente, consolidación o conciliación)
201Caso creado
400Cuerpo mal formado o validación fallida. Corrija la solicitud antes de reintentar
401Token de API ausente o inválido, o firma HMAC fallida
403El alcance del token no cubre el endpoint
404Un código referenciado no existe (por ejemplo, un tipo_alerta de AML desconocido)
409Conflicto con el estado actual del caso
429Límite de solicitudes superado. Reintente después de la ventana
500Error interno. Reintente con backoff y cite el request_id a soporte
503Una dependencia no está disponible. Reintente con backoff
codeEstado habitualDescripción
BAD_REQUEST400Un campo no pasó la validación; message indica cuál
VALIDATION_ERROR400El cuerpo está vacío o no es JSON válido
UNAUTHORIZED401Problema de credenciales
FORBIDDEN403Problema de alcance
NOT_FOUND404Código de referencia desconocido
CONFLICT409Conflicto de estado
DUPLICATE_EXTERNAL_ID409El identificador de ingesta ya fue usado en esa mesa
ALREADY_EXISTS409La mesa AML ya contiene una alerta con ese alerta_externo_id
RATE_LIMITED429Límite de solicitudes superado
SERVICE_UNAVAILABLE503Dependencia no disponible
INTERNAL_ERROR500Fallo inesperado

Manejo recomendado: trate 400, 401, 403 y 404 como permanentes para esa solicitud (avise al operador, no reintente a ciegas); trate 429, 500 y 503 como transitorios (reintente con backoff exponencial, ya que las claves de idempotencia hacen que los reintentos sean seguros).

MétodoRutaAutenticaciónDescripción
POST/api/v1/ingest/fraudeToken de API (ingest:fraude)Abrir un caso en la mesa Fraude
POST/api/v1/ingest/medToken de API (ingest:med)Abrir un caso en la mesa MED
POST/api/v1/ingest/pldToken de API (ingest:pld)Abrir o consolidar un caso en la mesa AML
POST/api/v1/ingest/kycToken de API (ingest:kyc) + HMAC opcionalAbrir o conciliar un caso en la mesa Cadastro PF o Cadastro PJ
GET/api/v1/api-tokensSesión de administradorListar los tokens de API
POST/api/v1/api-tokensSesión de administradorCrear un token de API
PATCH/api/v1/api-tokens/{id}/toggleSesión de administradorActivar o desactivar un token
DELETE/api/v1/api-tokens/{id}Sesión de administradorEliminar un token
GET/api/v1/settings/webhooksSesión de administradorCatálogo de endpoints entrantes
GET/healthz, /readyz, /livezNingunaSondas de liveness y readiness

Versión 18, actualizada el