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.
Descripción general
Sección titulada «Descripción general»La integración involucra a cuatro participantes:
| Participante | Responsabilidad |
|---|---|
| Sistema de origen | Detecta 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 Guardline | Valida, deduplica, enruta el caso a una mesa y una cola, calcula el SLA y lo distribuye a un analista |
| Consola de CMP de Guardline | Consola 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.
| Mesa | Endpoint | Alcance del token | Identificador del caso |
|---|---|---|---|
| Fraude | /api/v1/ingest/fraude | ingest:fraude | external_id |
| MED (mecanismo especial de devolución) | /api/v1/ingest/med | ingest:med | id, event_id, notification_id o med_id |
| AML (prevención del lavado de dinero) | /api/v1/ingest/pld | ingest:pld | alerta_externo_id |
| Onboarding, personas físicas (KYC) y personas jurídicas (KYB) | /api/v1/ingest/kyc | ingest:kyc | execution_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.
Entornos y autenticación
Sección titulada «Entornos y autenticación»Entornos
Sección titulada «Entornos»| Entorno | URL base |
|---|---|
| Sandbox | https://{instance}.cmp.dev.guardline.com.br |
| Producción | https://{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.
Tokens de API
Sección titulada «Tokens de API»Cada llamada de ingesta requiere el encabezado X-API-Key con un token de API.
X-API-Key: cmp_e188905ec47a80ec035fe31a3264bf1a5f9231f1353ea24717cf311740305f11Los 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:
| Atributo | Descripción |
|---|---|
label | Nombre en texto libre, por ejemplo fpp-producao u onp-webhook-kyc |
scope | La mesa sobre la que el token puede escribir. Ver Alcances |
rate_limit_max, rate_limit_window_seconds | Límite de solicitudes por token. Por defecto, 60 solicitudes cada 60 segundos |
expires_in_days | Vencimiento opcional. 0 (el valor por defecto) significa que el token nunca vence |
Un problema de credenciales siempre responde 401 o 403, nunca 400:
| Estado | Mensaje | Causa |
|---|---|---|
| 401 | missing api key | El encabezado X-API-Key no fue enviado o está vacío |
| 401 | invalid api key | El token es desconocido, fue desactivado, fue eliminado o venció |
| 403 | token scope not authorized for this endpoint | El 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.
Alcances
Sección titulada «Alcances»| Alcance | Endpoints permitidos |
|---|---|
ingest:fraude | POST /api/v1/ingest/fraude |
ingest:med | POST /api/v1/ingest/med |
ingest:pld | POST /api/v1/ingest/pld |
ingest:kyc | POST /api/v1/ingest/kyc (los casos KYC y KYB comparten este endpoint y este alcance) |
ingest | Los 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.
Límite de solicitudes
Sección titulada «Límite de solicitudes»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.
Encabezado de tenant
Sección titulada «Encabezado de tenant»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.
Formato de respuesta
Sección titulada «Formato de respuesta»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"}| Campo | Descripción |
|---|---|
error | Siempre true en una respuesta de error |
message | Motivo legible por personas. Los mensajes de validación están en portugués o en inglés según la mesa |
code | Código estable legible por máquina. Ver Códigos de error |
request_id | Identificador 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.
Idempotencia
Sección titulada «Idempotencia»Cada endpoint de ingesta es idempotente sobre una clave natural elegida por el sistema de origen, con alcance a la mesa:
| Mesa | Clave natural |
|---|---|
| Fraude | external_id |
| MED | id, 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 |
| AML | alerta_externo_id |
| Onboarding | execution_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.
Alertas de fraude
Sección titulada «Alertas de fraude»POST /api/v1/ingest/fraudeAlcance: 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).
Campos de la solicitud
Sección titulada «Campos de la solicitud»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.
| Campo | Tipo | Obligatorio | Alias | Descripción |
|---|---|---|---|---|
external_id | string | sí | id, event_id | Identificador único de la alerta en el sistema de origen. Clave de idempotencia |
subject_document | string | sí | documento | CPF 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_amount | number | sí | amount, valor | Importe en BRL, debe ser mayor que cero. Se almacena en centavos |
subject_name | string | no | cliente_nome, nome | Nombre del cliente |
counterparty_name | string | no | contraparte_nome | Nombre de la contraparte (beneficiario) |
transaction_type | string | no | tipo_transacao | Tipo de transacción, en minúsculas. Por defecto, pix |
risk_level | string | no | risco | Solo 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 |
score | integer | no | Score del motor en una escala de 0 a 1000. Ver Score, riesgo y prioridad | |
priority | integer | no | De 0 a 9, cuanto menor más urgente. Por defecto, 3. Los valores fuera del rango se ajustan al límite | |
sla_deadline | string (RFC 3339) | no | Plazo 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_analysis | object | no | Explicación completa de la decisión y contexto del cliente. Ver El bloque fraud_analysis |
Score, riesgo y prioridad
Sección titulada «Score, riesgo y prioridad»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ás | critical |
| de 850 a 949 | high |
| de 700 a 849 | medium |
| menos de 700 | low |
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.
El bloque fraud_analysis
Sección titulada «El bloque fraud_analysis»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.
Ejemplos
Sección titulada «Ejemplos»Alerta mínima:
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": { "...": "..." } }}Respuestas
Sección titulada «Respuestas»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.
Errores
Sección titulada «Errores»| Estado | message | Causa |
|---|---|---|
| 400 | empty body | No se envió ningún cuerpo |
| 400 | invalid json | El cuerpo no es JSON válido |
| 400 | external_id é obrigatório | Falta el identificador |
| 400 | subject_document é obrigatório | Falta el documento del cliente |
| 400 | transaction_amount deve ser maior que zero | Importe ausente, no numérico o no positivo |
| 401 | missing api key, invalid api key | Ver Tokens de API |
| 403 | token scope not authorized for this endpoint | El alcance del token no es ingest:fraude ni ingest |
| 429 | ingest rate limit exceeded | Límite por token superado |
Solicitudes MED
Sección titulada «Solicitudes MED»POST /api/v1/ingest/medAlcance: 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.
Campos de la solicitud
Sección titulada «Campos de la solicitud»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.
| Campo | Tipo | Obligatorio | Alias | Descripción |
|---|---|---|---|---|
valor_solicitado | number | sí | amount, valor | Importe solicitado para la devolución, en BRL, mayor que cero. Se almacena en centavos |
id | string o number | recomendado | event_id, notification_id, med_id | Identificador 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_id | string | no | med_request_id, id | Nú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_at | string (RFC 3339) | no | created_at, event_at | Momento en que se abrió la solicitud. Por defecto, el momento de la ingesta |
motivo | string | no | reason_code, reason, tipo | golpe (por defecto), fraude o erro_operacional. Los valores desconocidos se resuelven como golpe |
status | string | no | med_status | aberta (por defecto), em_analise, aguardando_instituicao, aprovado, negado o recusado. Los valores desconocidos se resuelven como aberta |
prazo_limite_at | string (RFC 3339) | no | deadline_at | Plazo regulatorio de respuesta de la solicitud MED |
recebedora | object | no | recipient, counterparty | Institució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_respondeu | boolean | no | Indica si la institución receptora ya respondió | |
valor_devolvido | number | no | Importe ya devuelto, en BRL | |
data_devolucao_at | string (RFC 3339) | no | Momento en que ocurrió la devolución | |
subject_name | string | no | cliente_nome, nome | Nombre del cliente |
subject_document | string | no | cliente_documento, documento | CPF o CNPJ del cliente |
transaction_type | string | no | tipo_transacao | Por defecto, pix |
risk_level | string | no | risk | low, medium, high o critical. Los valores desconocidos se ignoran |
score | integer | no | De 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 | |
priority | integer | no | De 0 a 9, cuanto menor más urgente. Por defecto, 3 |
Ejemplo
Sección titulada «Ejemplo»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 }'Respuestas
Sección titulada «Respuestas»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}Errores
Sección titulada «Errores»| Estado | message | Causa |
|---|---|---|
| 400 | empty body, invalid json | Solicitud mal formada |
| 400 | missing or invalid valor_solicitado | Importe ausente, no numérico o no mayor que cero |
| 401 | missing api key, invalid api key | Ver Tokens de API |
| 403 | token scope not authorized for this endpoint | El alcance del token no es ingest:med ni ingest |
| 429 | ingest rate limit exceeded | Límite por token superado |
Alertas AML
Sección titulada «Alertas AML»POST /api/v1/ingest/pldAlcance: 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.
Campos de la solicitud
Sección titulada «Campos de la solicitud»| Campo | Tipo | Obligatorio | Alias | Descripción |
|---|---|---|---|---|
alerta_externo_id | string | sí | 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_cnpj | string | sí | CPF (11 dígitos) o CNPJ (14 dígitos) del cliente investigado. La puntuación se elimina antes de la validación | |
tipo_alerta | string | sí | Código del tipo de alerta, sin distinción de mayúsculas y minúsculas. Ver Tipos de alerta | |
subject_name | string | recomendado | cliente_nome, nome | Nombre del cliente |
score_risco | number | recomendado | score | Score 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_total | number | recomendado | valor | Importe total del movimiento, en BRL. Lo usa la regla de COE |
valor_total_centavos | integer | no | valor_centavos | El mismo importe en centavos. Tiene precedencia sobre valor_total cuando ambos están presentes |
tipologia_sugerida | string[] | no | Códigos de tipología sugeridos por el motor, por ejemplo ["PIX001", "EST001"] | |
dados_cliente | object | no | Contexto libre del cliente (ingresos declarados, actividad, ciudad, estado). Se almacena textualmente y se muestra en el expediente | |
dados_transacionais | object | no | Contexto transaccional libre (cantidad de operaciones, período, canales). Se almacena textualmente | |
contrapartes | array | no | Contrapartes involucradas. Se almacenan textualmente | |
is_coe | boolean | no | Fuerza la clasificación como COE automática sin importar el tipo ni el importe | |
priority | integer | no | De 0 a 9, cuanto menor más urgente. Por defecto, 3. Una COE automática siempre queda en prioridad 1 | |
fpp_source_url | string | no | Enlace directo a la alerta en el sistema de origen, mostrado en el expediente |
Tipos de alerta
Sección titulada «Tipos de alerta»tipo_alerta debe ser uno de los códigos siguientes. Un código desconocido se rechaza con 404.
| Código | Descripción |
|---|---|
pix | Transferencia PIX |
ted | Transferencia TED |
doc | Transferencia DOC |
boleto | Pago de boleto |
especie | Movimiento en efectivo (billetes). El único tipo elegible para la regla de COE automática |
cambio | Operación de cambio de divisas |
cartao | Operación con tarjeta |
deposito | Depósito |
saque | Retiro |
transferencia | Transferencia genérica entre cuentas |
outro | Cualquier otro movimiento |
Pipeline de procesamiento
Sección titulada «Pipeline de procesamiento»Cada alerta pasa por cuatro pasos dentro de una única transacción:
- Idempotencia. Si ya existe una alerta con el mismo
alerta_externo_id, la API responde 200 conis_new_case: falseyconsolidated_intoapuntando 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ódigoALREADY_EXISTS, que es seguro tratar como un duplicado. - Detección de COE. La alerta es una COE automática cuando
is_coeestrue, o cuandotipo_alertaesespeciey 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 AMLpendente_geracao_xmlcon prioridad 1 y se proyecta comodecided/aprovadoen el estado genérico del caso, de modo que aparece como lista para el paso de comunicación al COAF. - Consolidación. Cuando la alerta no es una COE y el mismo
cpf_cnpjya tiene un caso AML activo (cualquier estado AML distinto dearquivado_triagem,arquivado,comunicado_coaf,retificadoocancelado), la alerta se adjunta a ese caso. La API responde 200 conis_new_case: falseyconsolidated_intoapuntando 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_alertaigual aespecie, o el indicador explícitois_coe): el umbral de importe por sí solo no la activa. - Caso nuevo. En caso contrario se crea un caso AML nuevo con estado
new, estado de negocioaguardando_analise, estado AMLnovo, un plazo de selección de 45 días (prazo_selecao) y un número de caso legible con la formaPLD-YYYY-NNNNN. La API responde 201.
Ejemplos
Sección titulada «Ejemplos»Alerta mínima:
{ "alerta_externo_id": "ALR-2026-000981", "cpf_cnpj": "12345678901", "tipo_alerta": "pix"}Alerta recomendada:
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}Respuestas
Sección titulada «Respuestas»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"}| Campo | Descripción |
|---|---|
case_id | UUID del caso que contiene la alerta |
case_pld_detail_id | UUID del registro de detalle AML de ese caso |
numero_caso | Número de caso legible, el identificador que citan los operadores |
is_new_case | true cuando esta llamada creó un caso |
is_coe_automatica | true 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_into | Presente 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_id | Eco 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.
Errores
Sección titulada «Errores»| Estado | message | Causa |
|---|---|---|
| 400 | empty body, invalid json | Solicitud mal formada |
| 400 | alerta_externo_id is required | Falta el identificador |
| 400 | cpf_cnpj must be a valid 11 or 14-digit document | Tras eliminar los caracteres no numéricos, el documento no tiene 11 ni 14 caracteres |
| 400 | tipo_alerta is required | Falta el tipo de alerta |
| 404 | pld_alert_type "xyz" not found | tipo_alerta no es uno de los tipos de alerta; el mensaje cita el valor que fue enviado |
| 401 | missing api key, invalid api key | Ver Tokens de API |
| 403 | token scope not authorized for this endpoint | El alcance del token no es ingest:pld ni ingest |
| 429 | ingest rate limit exceeded | Límite por token superado |
Casos de onboarding (KYC y KYB)
Sección titulada «Casos de onboarding (KYC y KYB)»POST /api/v1/ingest/kycAlcance: 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:
| Forma | Cuándo | Discriminador |
|---|---|---|
| Sobre de onboarding | El 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ón | event_type es cmp.case.review, cmp.case.approved o cmp.case.rejected |
| Payload KYC genérico | Un motor de decisión de terceros envía su análisis KYC completo de una persona física | event_type ausente o con cualquier otro valor |
Ambas formas responden con el mismo cuerpo { case_id, external_id, created }.
Firma de la solicitud (HMAC)
Sección titulada «Firma de la solicitud (HMAC)»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.
| Encabezado | Valor |
|---|---|
X-Guardline-Timestamp | Tiempo Unix en segundos (entero) en el momento de la firma |
X-Guardline-Signature | HMAC-SHA256 de {timestamp}.{body} en hexadecimal minúsculo |
Procedimiento de firma:
- 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.
- Concatene el timestamp, un punto literal y el cuerpo sin procesar:
{timestamp}.{body}. - Calcule el HMAC-SHA256 de esa cadena con el secreto compartido y codifique el digest en hexadecimal.
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.
Sobre de onboarding
Sección titulada «Sobre de onboarding»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.
Semántica de los eventos
Sección titulada «Semántica de los eventos»event_type | Efecto |
|---|---|
cmp.case.review | Crea 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.approved | Concilia 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.rejected | Igual 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ó.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
event_type | string | sí | Ver Semántica de los eventos |
execution_id | string | sí | Identificador de la ejecución de onboarding. Se almacena como el external_id del caso y se usa como clave de idempotencia |
flow_type | string | sí | 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 |
subject | object | sí | Ver abajo |
case_envelope | object | sí | Ver abajo |
event_id | string | no | Identificador del evento del lado productor, almacenado junto con el caso |
fired_at | string (RFC 3339) | no | Momento en que el productor emitió el evento |
tenant_id | string | no | Tenant del productor, informativo |
flow_id, flow_group_code | string | no | Flujo y grupo de flujo de la ejecución, informativos |
edd_flagged | boolean | no | La ejecución requiere diligencia debida reforzada. Se muestra al analista |
rejection_reasons | string[] | no | Motivos en texto libre detrás de un rechazo. Se usan para clasificar el código de decisión del rechazo |
derivation_reasons | object[] | no | Por qué el motor derivó el caso a análisis manual: { "code", "label_ptbr", "severity", "source", "detail" }. Se renderiza textualmente en el expediente |
representative | object | no | Solo kyc_minor: { "execution_id", "status" } de la ejecución del representante legal |
guardian_type | string | no | Solo kyc_minor: parent, mother, father u other |
representatives | object[] | no | Solo 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:
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
document | string | solo en eventos terminales | CPF 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_type | string | no | cpf o cnpj |
name | string | no | Nombre del sujeto, mostrado en la cola |
email, phone, birth_date | string | no | Se capturan en el payload del caso |
case_envelope:
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
priority | string | sí | 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_status | string | no | Estado del motor: approved, rejected, blocked, escalated u otro. Determina la decisión de workflow que se muestra al analista (ver el mapeo abajo) |
decision_code | string | no | Decisió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_level | string | no | low, medium, high o critical, sin distinción de mayúsculas y minúsculas. Los valores desconocidos se ignoran |
total_score | number | no | Score 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_at | string (RFC 3339) | no | Marcas de tiempo del productor. completed_at se convierte en el momento de la decisión en un evento terminal |
Mapeo al caso
Sección titulada «Mapeo al caso»| Origen | Atributo del caso | Regla |
|---|---|---|
case_envelope.case_status | Decisión de workflow mostrada en la mesa | approved pasa a ALLOW; rejected y blocked pasan a BLOCK; cualquier otro valor pasa a REVIEW |
case_envelope.decision_code + rejection_reasons | Código de decisión terminal | approved 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.priority | Prioridad del caso | De P0 a P3 se mapean de 0 a 3 |
case_envelope.risk_level | Nivel de riesgo del caso | Se pasa a minúsculas; los valores desconocidos dejan el nivel de riesgo sin definir |
case_envelope.total_score | Score del caso | Se redondea y se limita al rango de 0 a 100 |
subject.name, subject.document | Columnas de la cola | Se 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.
Ejemplos
Sección titulada «Ejemplos»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"]}Payload KYC genérico
Sección titulada «Payload KYC genérico»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.
Campos obligatorios
Sección titulada «Campos obligatorios»| Campo | Tipo | Descripción |
|---|---|---|
case_id | string | Identificador único del análisis en el sistema de origen. Clave de idempotencia. Se aceptan external_id e id como alias |
subject.personal.cpf | string | CPF 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.finalDecision | string | ALLOW, REVIEW o BLOCK, sin distinción de mayúsculas y minúsculas. Se acepta decision.final_decision |
Bloques opcionales
Sección titulada «Bloques opcionales»| Bloque | Campos que lee el CMP |
|---|---|
subject.personal | name (también subject.basicData.name, subject.cpf.name, subject_name en el nivel raíz) |
decision | finalScore (de 0 a 100), thresholds.review, thresholds.block, workflowExplanation.{workflowId, workflowName, totalScore, reviewThreshold, blockThreshold, executionTimeMs, executedAt, stagesResults[]} |
trust | riskScore, riskLabel |
facematch | matched, confidenceScore |
liveness | mode |
facial | riskScore, riskLevel |
documentoscopia | qualityScore, confidenceScore, isExpired |
compliance | isPep, sanctions[], lawsuits[], govDebts[], mediaExposureUrls[] |
contact | mediaExposureUrls[] |
device | riskScore, isVpn, isEmulator, isRooted |
financial.financialRisk | risk_level, financial_risk_score, is_on_collection |
payload_version | Etiqueta de versión del contrato, v1 por defecto |
Atributos derivados
Sección titulada «Atributos derivados»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:
| Atributo | Regla |
|---|---|
| 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 riesgo | BLOCK 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 |
| Score | decision.finalScore (o workflowExplanation.totalScore), limitado al rango de 0 a 100 |
Ejemplo
Sección titulada «Ejemplo»{ "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).
Respuestas
Sección titulada «Respuestas»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}Errores
Sección titulada «Errores»| Estado | message | Causa |
|---|---|---|
| 400 | empty body, invalid json | Solicitud mal formada |
| 400 | execution_id e obrigatorio | Sobre sin execution_id |
| 400 | flow_type "x" invalido (esperado kyc, kyb, kyc_minor ou kyc_representative) | flow_type desconocido |
| 400 | subject e obrigatorio, case_envelope e obrigatorio | Faltan objetos del sobre |
| 400 | subject.document e obrigatorio | Evento terminal sin documento |
| 400 | case_envelope.priority "x" invalida (esperado P0, P1, P2 ou P3) | Prioridad fuera del conjunto aceptado |
| 400 | case_id e obrigatorio | Payload genérico sin identificador |
| 400 | subject.personal.cpf e obrigatorio | Payload genérico sin CPF |
| 400 | decision.finalDecision deve ser ALLOW, REVIEW ou BLOCK | Payload genérico con una decisión inválida |
| 401 | missing signature, invalid signature, stale timestamp y relacionados | Capa HMAC habilitada y firma fallida |
| 401 | missing api key, invalid api key | Ver Tokens de API |
| 403 | token scope not authorized for this endpoint | El alcance del token no es ingest:kyc ni ingest |
| 429 | ingest rate limit exceeded | Límite por token superado |
Ciclo de vida del caso
Sección titulada «Ciclo de vida del caso»Mesas y colas
Sección titulada «Mesas y colas»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 mesa | Mesa | Tipo de caso | Endpoint de ingesta |
|---|---|---|---|
med | MED | med | /api/v1/ingest/med |
fraude | Fraude | fraude | /api/v1/ingest/fraude |
kyc | Cadastro PF | kyc | /api/v1/ingest/kyc |
kyb | Cadastro PJ | kyb | /api/v1/ingest/kyc |
pld | AML | pld | /api/v1/ingest/pld |
comercial | Comercial | conserva el tipo de la mesa de origen | ninguno, recibe casos por transferencia |
Estados
Sección titulada «Estados»Cada caso lleva un estado genérico, compartido por todas las mesas, y un estado de negocio que lo refina:
| Estado genérico | Significado |
|---|---|
new | Creado por la ingesta, a la espera de distribución o de ser tomado |
in_analysis | Asignado a un analista |
escalated | Enviado a la cola de supervisión |
decided | Se registró una decisión |
closed | Cerrado |
failed | La 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.
Transferencias entre mesas
Sección titulada «Transferencias entre mesas»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:
| Desde | Hacia |
|---|---|
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.
Retorno de la decisión
Sección titulada «Retorno de la decisión»Cómo se entera el sistema de origen de la decisión del analista depende de la mesa.
Casos de onboarding
Sección titulada «Casos de onboarding»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.
Casos AML: webhooks salientes
Sección titulada «Casos AML: webhooks salientes»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.
Eventos
Sección titulada «Eventos»| Evento | Se emite cuando | pld_status resultante |
|---|---|---|
triagem_concluida | La 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 evento | arquivado_triagem o arquivado |
comite_aprovado | La sesión del comité cerró con la aprobación de la comunicación | aprovado_comite |
comite_reprovado | La sesión del comité cerró rechazando la comunicación | reprovado_comite |
coaf_registrado | La comunicación al COAF fue registrada (protocolo SISCOAF recibido) | comunicado_coaf |
coaf_erro | El envío a SISCOAF falló y el error quedó registrado | erro_siscoaf |
coaf_retificado | La comunicación fue rectificada | retificado |
coaf_cancelado | La comunicación fue cancelada | cancelado |
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.
Payload
Sección titulada «Payload»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" }}| Campo | Descripción |
|---|---|
event | Nombre del evento, según la tabla anterior |
delivery_id | Identificador estable de esta entrega, idéntico en todos los reintentos. Úselo para deduplicar |
correlation_key | El número del caso AML |
case_id | UUID del caso |
attempt | 1 en la primera entrega, 2 y 3 en los reintentos |
timestamp | Momento en que se envió este intento |
payload.action | La acción de workflow que produjo la transición (por ejemplo pld_arquivar_triagem, pld_registrar_coaf, pld_retificar, comite_aprovado) |
payload.pld_status | El estado AML resultante |
payload.motivo | Presente solo en coaf_retificado y coaf_cancelado: la justificación introducida por el operador |
payload.occurred_at | Momento en que se confirmó la transición |
Encabezados
Sección titulada «Encabezados»| Encabezado | Descripción |
|---|---|
Content-Type | Siempre application/json |
X-Webhook-Event | El nombre del evento, para enrutar sin analizar el cuerpo |
X-Webhook-Delivery | El identificador de la entrega, el mismo valor que delivery_id en el cuerpo |
X-Webhook-Signature | HMAC-SHA256 del cuerpo sin procesar, en hexadecimal |
Verificación de la firma
Sección titulada «Verificación de la firma»La firma cubre únicamente el cuerpo sin procesar de la solicitud (no hay componente de timestamp, a diferencia de la firma entrante de onboarding).
- Lea los bytes del cuerpo sin procesar, antes de cualquier análisis JSON.
- Calcule el HMAC-SHA256 del cuerpo con el secreto del receptor y codifíquelo en hexadecimal.
- Compare con
X-Webhook-Signatureusando 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)Política de reintentos
Sección titulada «Política de reintentos»Una entrega es exitosa cuando el receptor responde con cualquier estado 2xx en menos de 10 segundos. En caso contrario, el CMP reintenta:
| Intento | Espera tras el fallo anterior |
|---|---|
| 2 | En el siguiente vaciado posterior al fallo |
| 3 | Al 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.
Configuración del endpoint
Sección titulada «Configuración del endpoint»| Requisito | Detalle |
|---|---|
| Protocolo | Se exige HTTPS |
| Versión de TLS | 1.2 o superior |
| Respuesta de éxito | Cualquier código 2xx, el cuerpo se ignora |
| Tiempo de espera | Responder en menos de 10 segundos |
| Idempotencia | Deduplicar 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.
Casos de fraude y MED
Sección titulada «Casos de fraude y MED»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.
Administración de tokens
Sección titulada «Administración de tokens»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.
Listar tokens
Sección titulada «Listar tokens»GET /api/v1/api-tokensDevuelve 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.
Crear un token
Sección titulada «Crear un token»POST /api/v1/api-tokens{ "label": "fpp-producao", "scope": "ingest:fraude", "rate_limit_max": 300, "rate_limit_window_seconds": 60, "expires_in_days": 365}| Campo | Obligatorio | Descripción |
|---|---|---|
label | sí | Nombre de visualización |
scope | no | Uno de los alcances. Por defecto, ingest |
rate_limit_max | no | Solicitudes por ventana. Por defecto, 60 |
rate_limit_window_seconds | no | Duración de la ventana. Por defecto, 60 |
expires_in_days | no | Dí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.
Rotar, desactivar y eliminar
Sección titulada «Rotar, desactivar y eliminar»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.
Catálogo de webhooks entrantes
Sección titulada «Catálogo de webhooks entrantes»GET /api/v1/settings/webhooksDevuelve 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.
Diagramas de flujo completos
Sección titulada «Diagramas de flujo completos»Alerta de fraude desde un motor externo
Sección titulada «Alerta de fraude desde un motor externo»Caso de onboarding derivado por el ONP
Sección titulada «Caso de onboarding derivado por el ONP»Alerta AML con comunicación al COAF
Sección titulada «Alerta AML con comunicación al COAF»Referencia de errores
Sección titulada «Referencia de errores»Códigos de estado HTTP
Sección titulada «Códigos de estado HTTP»| Estado | Significado |
|---|---|
| 200 | Solicitud procesada; el caso ya existía (repetición idempotente, consolidación o conciliación) |
| 201 | Caso creado |
| 400 | Cuerpo mal formado o validación fallida. Corrija la solicitud antes de reintentar |
| 401 | Token de API ausente o inválido, o firma HMAC fallida |
| 403 | El alcance del token no cubre el endpoint |
| 404 | Un código referenciado no existe (por ejemplo, un tipo_alerta de AML desconocido) |
| 409 | Conflicto con el estado actual del caso |
| 429 | Límite de solicitudes superado. Reintente después de la ventana |
| 500 | Error interno. Reintente con backoff y cite el request_id a soporte |
| 503 | Una dependencia no está disponible. Reintente con backoff |
Códigos de error
Sección titulada «Códigos de error»code | Estado habitual | Descripción |
|---|---|---|
BAD_REQUEST | 400 | Un campo no pasó la validación; message indica cuál |
VALIDATION_ERROR | 400 | El cuerpo está vacío o no es JSON válido |
UNAUTHORIZED | 401 | Problema de credenciales |
FORBIDDEN | 403 | Problema de alcance |
NOT_FOUND | 404 | Código de referencia desconocido |
CONFLICT | 409 | Conflicto de estado |
DUPLICATE_EXTERNAL_ID | 409 | El identificador de ingesta ya fue usado en esa mesa |
ALREADY_EXISTS | 409 | La mesa AML ya contiene una alerta con ese alerta_externo_id |
RATE_LIMITED | 429 | Límite de solicitudes superado |
SERVICE_UNAVAILABLE | 503 | Dependencia no disponible |
INTERNAL_ERROR | 500 | Fallo 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).
Resumen de endpoints
Sección titulada «Resumen de endpoints»| Método | Ruta | Autenticación | Descripción |
|---|---|---|---|
| POST | /api/v1/ingest/fraude | Token de API (ingest:fraude) | Abrir un caso en la mesa Fraude |
| POST | /api/v1/ingest/med | Token de API (ingest:med) | Abrir un caso en la mesa MED |
| POST | /api/v1/ingest/pld | Token de API (ingest:pld) | Abrir o consolidar un caso en la mesa AML |
| POST | /api/v1/ingest/kyc | Token de API (ingest:kyc) + HMAC opcional | Abrir o conciliar un caso en la mesa Cadastro PF o Cadastro PJ |
| GET | /api/v1/api-tokens | Sesión de administrador | Listar los tokens de API |
| POST | /api/v1/api-tokens | Sesión de administrador | Crear un token de API |
| PATCH | /api/v1/api-tokens/{id}/toggle | Sesión de administrador | Activar o desactivar un token |
| DELETE | /api/v1/api-tokens/{id} | Sesión de administrador | Eliminar un token |
| GET | /api/v1/settings/webhooks | Sesión de administrador | Catálogo de endpoints entrantes |
| GET | /healthz, /readyz, /livez | Ninguna | Sondas de liveness y readiness |
Versión 18, actualizada el