Flujos de automatizaciónDisparadores

Disparadores

Referencia completa de cada disparador de los flujos de trabajo de automatización de Certivant: cuándo se activa cada evento, su contexto de datos, ejemplos de cargas útiles y la configuración de sujeto, alcance y filtros.

Un disparador es el único nodo de entrada de cada flujo de trabajo de automatización. Declara qué evento inicia el flujo, a qué objetos debe afectar ese evento (el sujeto y el alcance) y un filtro opcional que restringe aún más qué eventos cumplen las condiciones. Cuando la plataforma emite un evento que coincide, se crea un KycWorkflowRun y el flujo se ejecuta nodo por nodo.

Esta página documenta cada disparador que el motor emite realmente, a partir de los puntos de emisión de la plataforma. Cada entrada indica cuándo se activa, el context exacto que transporta, una carga útil de ejemplo real y cómo limitarlo y filtrarlo.

El catálogo de disparadores es una enumeración fija del motor (WorkflowTriggerEvent). El selector del editor, esta página, el esquema de validación de la API y el despachador leen de la misma lista, así que cada disparador que puede seleccionar es un disparador que la plataforma emite realmente; ninguno es decorativo. Algunas capacidades que todavía no tienen un punto de emisión se dejan fuera del selector a propósito y aparecen en Disparadores previstos con la alternativa que sí funciona, para que nunca construya un flujo que nunca se ejecute sin avisar.


Cómo se activan los disparadores

Los disparadores no se consultan periódicamente. Cada uno corresponde a un punto real de la plataforma donde el código llama al despachador:

emisor (ruta de API / worker / cron)
   └─ enqueueWorkflowDispatch({ organizationId, event, kycRequestId?, …, context })
          └─ workflow.worker → dispatchWorkflowEvent(input)
                 ├─ cargar los flujos activos cuyo trigger.event === input.event
                 ├─ restringir por sujeto y alcance      (formulario / solicitante / documento / comprobación)
                 ├─ evaluar el filtro DSL opcional        (sobre input.context)
                 └─ crear y ejecutar un KycWorkflowRun por cada coincidencia

Tres propiedades son importantes para quienes integran la plataforma:

  • Envío sin espera de respuesta. El envío está envuelto de forma que un fallo nunca interrumpe la acción original. Si la carga de un documento activa un flujo y el envío lanza una excepción, la carga se completa igualmente. Trate los efectos secundarios de los flujos como ejecutados eventualmente, no de forma síncrona con la llamada a la API.
  • Un evento, muchas ejecuciones. Un solo evento puede iniciar todos los flujos activos que coincidan con él. Use el sujeto, el alcance y los filtros para que esta multiplicación sea intencionada.
  • El contexto se captura una sola vez. El objeto context del evento se guarda en la primera entrada del registro de la ejecución. Los nodos Condición, las plantillas de Acción y los marcadores de combinación de correspondencia leen esa instantánea; no vuelven a consultar el estado en vivo.

El sobre de envío

Cada emisión produce un WorkflowEventInput. Los campos además de organizationId y event son opcionales y dependen del evento:

CampoTipoPresente cuando
organizationIdstringsiempre
eventWorkflowTriggerEventsiempre
kycRequestIdstring | nullel evento concierne a una verificación concreta (la mayoría de eventos)
formIdstring | nullform.submitted
formKind"kyc" | "kyb" | nullform.submitted
applicantIdstring | nulleventos con alcance de solicitante
documentTypestring | nulldocument.*
checkKindstring | nullcheck.completed, liveness.*, sanctions.*
contextobjecthechos específicos del evento (véase cada disparador más abajo)
workflowIdstringsolo en una invocación directa de trigger_workflow
chainDepthnumberprofundidad de encadenamiento de flujo a flujo (máximo 5)

subject, scope, filter, formIds, schedule, scoreMin y scoreMax son propiedades del disparador que usted configura, no del evento en tiempo de ejecución. El despachador los usa para decidir si un evento entrante coincide con su flujo de trabajo.


La definición del disparador

Esta es la forma que se guarda en el trigger de un flujo de trabajo (WorkflowTrigger):

interface WorkflowTrigger {
  event: WorkflowTriggerEvent;          // which event starts the workflow
  subject?: "form" | "applicant"        // the kind of object it binds to
          | "document" | "check";       //   (absent → legacy org-wide)
  scope?: {
    mode: "specific" | "category" | "all";
    ids?: string[];                     // specific-mode object ids
    category?: string;                  // category-mode key (see below)
  };
  filter?: string;                      // optional DSL predicate over context
  formIds?: string[];                   // legacy form-binding (pre-subject)
  schedule?: string;                    // schedule only — cron expression
  scoreMin?: number;                    // score.threshold convenience bound
  scoreMax?: number;                    // score.threshold convenience bound
}

Sujeto y alcance

El sujeto es el tipo de objeto que vigila el disparador; el alcance lo restringe a objetos concretos, a una categoría o a todos. Así se evita que un disparador se active en toda la organización por accidente.

SujetoEventos válidosOpciones de scope.modeSignificado de category
formform.submittedspecific (ids de formulario) · category · all"kyc" o "kyb"
applicantrequest.*, score.threshold, edd.triggered, verification.expiredspecific (ids de solicitante) · all—
documentdocument.uploaded, document.failedspecific · category · allun tipo de documento, p. ej. "passport"
checkcheck.completed, liveness.*, sanctions.*specific · category · allun tipo de comprobación, p. ej. "sanctions"
(ninguno)schedule, inbound.webhook, monitoring.alert, kyb.*, reviewer.decision, conference.completed—en toda la organización

Modos de alcance:

  • specific: coincide solo con los ids indicados (por ejemplo, activarse solo para este formulario de incorporación). Si todos los ids de un alcance specific apuntan a objetos eliminados, el disparador queda huérfano: el despachador se niega a activarlo (permanece en reposo en lugar de recurrir en silencio a «todos»), y el editor muestra una insignia «Requiere reasignación».
  • category: coincide con una clase de objetos (todos los formularios KYC, todos los pasaportes, todas las comprobaciones de sanciones).
  • all: coincide con cualquier objeto del tipo de sujeto dentro de la organización.

Un disparador sin subject mantiene el comportamiento heredado: en toda la organización, opcionalmente restringido por formIds para form.submitted. Los flujos nuevos siempre deben definir un sujeto y un alcance.

Filtro (DSL)

filter es un predicado booleano opcional con la misma gramática de expresiones que el DSL de la política de decisión. Se evalúa sobre el context del evento. Un filtro vacío se activa con cada evento de ese tipo. Véase Filtrado con el DSL.


Categorías de disparadores

CategoríaDisparadores
Entradaform.submitted, request.created
Ciclo de vida / estadorequest.status_changed, request.approved, request.rejected, request.escalated, request.resubmitted, request.documents_ready
Documentosdocument.uploaded, document.failed
Señales de verificacióncheck.completed, liveness.passed, liveness.failed, face_match.passed, face_match.failed, sanctions.hit, sanctions.cleared, pep.match, kyb.match_found, kyb.match_not_found, kyb.ubo_completed
Revisión humanareviewer.decision, reviewer.assigned, reviewer.comment_added, edd.triggered
Riesgo / puntuaciónscore.threshold, score.updated
Tiempo / monitorizaciónschedule, verification.expired, monitoring.alert
Externo / integracióninbound.webhook, conference.completed

Referencia de disparadores

Cada entrada usa la misma estructura: Cuándo se activa, Contexto de datos, Evento de ejemplo y Ejemplo de uso en un flujo de trabajo. Cada carga útil es la forma de envío real que produce el código emisor.

Entrada

form.submitted

«Se envía un formulario» · sujeto form

Cuándo se activa. Un solicitante envía un formulario de verificación público y alojado. Se emite desde la ruta pública de formularios en el momento en que un envío crea su KycRequest. El evento complementario request.created se emite junto con él.

Contexto de datos.

CampoTipoNotas
typestringTipo de solicitante en el momento del envío, p. ej. "INDIVIDUAL"

Nivel superior: formId, formKind ("kyc"/"kyb"), kycRequestId, applicantId.

Evento de ejemplo.

{
  "organizationId": "org_01H...",
  "event": "form.submitted",
  "kycRequestId": "req_01J...",
  "formId": "form_01H...",
  "formKind": "kyc",
  "context": { "type": "INDIVIDUAL" }
}

Ejemplo de uso en un flujo de trabajo. Vincule un flujo de correo de bienvenida y solicitud de documentos a un único formulario de incorporación:

trigger:
  event: form.submitted
  subject: form
  scope: { mode: specific, ids: ["form_01H..."] }

request.created

«Se crea una verificación» · sujeto applicant

Cuándo se activa. Se crea una solicitud de verificación, ya sea mediante POST /api/v1/kyc/requests (201) o como complemento de un form.submitted alojado. Se activa antes de que se suba cualquier documento o se ejecute cualquier comprobación, así que úselo para enrutar la entrada, no para los resultados.

Contexto de datos.

CampoTipoNotas
applicant.countrystring | nullISO-3166 alpha-2, a partir de la nacionalidad del solicitante
applicant.typestring"INDIVIDUAL" o "BUSINESS"
typestringTipo de solicitud (INDIVIDUAL / BUSINESS)

Evento de ejemplo.

{
  "organizationId": "org_01H...",
  "event": "request.created",
  "kycRequestId": "req_01J...",
  "applicantId": "app_01J...",
  "context": {
    "applicant": { "country": "CA", "type": "INDIVIDUAL" },
    "type": "INDIVIDUAL"
  }
}

Ejemplo de uso en un flujo de trabajo. Enrute los solicitantes empresariales a una cola de ingreso KYB:

trigger:
  event: request.created
  subject: applicant
  scope: { mode: all }
  filter: 'applicant.type = "BUSINESS"'

Ciclo de vida / estado

Estos eventos se activan después de que la orquestación finaliza un veredicto. Todos transportan el mismo contexto de decisión. Los eventos específicos (approved/rejected/escalated) se activan además de el genérico request.status_changed, así que escuche uno u otro, no ambos, para evitar la doble ejecución.

Contexto de datos compartido (request.approved, request.rejected, request.escalated, request.status_changed):

CampoTipoNotas
statusstringNuevo estado, p. ej. "APPROVED", "REJECTED", "ESCALATED", "PENDING_SECOND_REVIEW"
decisionstring | nullVeredicto del motor de puntuación: "approve" / "reject" / "escalate"
overallScorenumber | nullPuntuación de confianza de 0 a 100 (más alta = menor riesgo)
applicant.countrystring | nullISO-3166 alpha-2
applicant.typestringINDIVIDUAL / BUSINESS

request.approved

«Se aprueba una verificación» · sujeto applicant

Cuándo se activa. La orquestación finaliza la solicitud como APPROVED.

{
  "organizationId": "org_01H...",
  "event": "request.approved",
  "kycRequestId": "req_01J...",
  "applicantId": "app_01J...",
  "context": {
    "status": "APPROVED",
    "decision": "approve",
    "overallScore": 88,
    "applicant": { "country": "US", "type": "INDIVIDUAL" }
  }
}

Ejemplo de uso en un flujo de trabajo. Genere y envíe por correo un informe de verificación solo para las aprobaciones de alta confianza:

trigger:
  event: request.approved
  subject: applicant
  scope: { mode: all }
  filter: 'overallScore >= 80'

request.rejected

«Se rechaza una verificación» · sujeto applicant

Cuándo se activa. La orquestación finaliza la solicitud como REJECTED. Contexto idéntico al de request.approved, con status: "REJECTED" y decision: "reject".

Ejemplo de uso en un flujo de trabajo. Notifique al gestor asignado y marque al solicitante en caso de rechazo:

trigger:
  event: request.rejected
  subject: applicant
  scope: { mode: all }

request.escalated

«Se escala una verificación» · sujeto applicant

Cuándo se activa. La orquestación (o la acción escalate de un flujo anterior) traslada la solicitud a ESCALATED / revisión humana. Contexto idéntico a la forma compartida, con status: "ESCALATED".

Ejemplo de uso en un flujo de trabajo. Programe una conferencia de identidad en vivo cada vez que se escale una solicitud:

trigger:
  event: request.escalated
  subject: applicant
  scope: { mode: all }

request.status_changed

«Cambia el estado de una verificación» · sujeto applicant

Cuándo se activa. Siempre se emite ante un cambio de estado finalizado, junto con cualquier evento de decisión específico. Úselo para reaccionar a estados que no tienen un evento dedicado (p. ej., PENDING_SECOND_REVIEW, CANCELLED).

Ejemplo de uso en un flujo de trabajo. Reaccione solo ante un traspaso a dos revisores:

trigger:
  event: request.status_changed
  subject: applicant
  scope: { mode: all }
  filter: 'status = "PENDING_SECOND_REVIEW"'

request.resubmitted

«Se reenvía una verificación» · sujeto applicant

Cuándo se activa. Un revisor devuelve una solicitud para que se vuelva a enviar desde el panel; la solicitud regresa a PENDING_UPLOAD.

Contexto de datos.

CampoTipoNotas
statusstring"PENDING_UPLOAD"
kindstring"resubmit"
{
  "organizationId": "org_01H...",
  "event": "request.resubmitted",
  "kycRequestId": "req_01J...",
  "context": { "status": "PENDING_UPLOAD", "kind": "resubmit" }
}

Ejemplo de uso en un flujo de trabajo. Vuelva a enviar al solicitante su enlace de carga e inicie un temporizador de recordatorio.

request.documents_ready

«Se han cargado todos los documentos requeridos» · sujeto applicant

Cuándo se activa. El solicitante acaba de cargar el último elemento de su lista de documentos requeridos, por lo que la solicitud pasa a DOCUMENTS_READY. Se emite desde ambas vías de carga (la API de documentos del panel y la sesión de verificación alojada), y solo en la carga que vacía el conjunto de documentos pendientes. Es la señal concreta de que «el solicitante completó todos los pasos requeridos».

Contexto de datos.

CampoTipoNotas
statusstring"DOCUMENTS_READY"
{
  "organizationId": "org_01H...",
  "event": "request.documents_ready",
  "kycRequestId": "req_01J...",
  "context": { "status": "DOCUMENTS_READY" }
}

Ejemplo de uso en un flujo de trabajo. Notifique al revisor asignado en el momento en que un caso esté listo para revisar:

trigger:
  event: request.documents_ready
  subject: applicant
  scope: { mode: all }

Documentos

document.uploaded

«Se carga un documento» · sujeto document

Cuándo se activa. Cada vez que se carga un documento, ya sea a través de la API del panel o de una sesión de verificación alojada. Un evento por documento.

Contexto de datos.

CampoTipoNotas
document.typestringp. ej. "passport", "id_card", "drivers_license"
document.sidestring | null"front" / "back" (null cuando no aplica)

Nivel superior: documentType, kycRequestId.

{
  "organizationId": "org_01H...",
  "event": "document.uploaded",
  "kycRequestId": "req_01J...",
  "documentType": "passport",
  "context": { "document": { "type": "passport", "side": "front" } }
}

Ejemplo de uso en un flujo de trabajo. Active el flujo solo con las cargas de comprobante de domicilio:

trigger:
  event: document.uploaded
  subject: document
  scope: { mode: category, category: "utility_bill" }

document.failed

«Un documento no supera la validación» · sujeto document

Cuándo se activa. La orquestación marca un documento como failed (el OCR no pudo ejecutarse, la autenticidad lo rechazó, etc.). Un evento por documento fallido. La forma del contexto es la misma que la de document.uploaded.

{
  "organizationId": "org_01H...",
  "event": "document.failed",
  "kycRequestId": "req_01J...",
  "documentType": "drivers_license",
  "context": { "document": { "type": "drivers_license", "side": "back" } }
}

Ejemplo de uso en un flujo de trabajo. Solicite automáticamente una nueva captura de cualquier documento fallido.

Señales de verificación

check.completed

«Se completa una comprobación de verificación» · sujeto check

Cuándo se activa. Una vez por cada comprobación individual que termina durante una ejecución de orquestación (OCR, coincidencia facial, prueba de vida, sanciones, autenticidad del documento, lectura NFC…). Es la señal genérica y de alta frecuencia; para un resultado claro y único, prefiera liveness.* / sanctions.*.

Multiplicación controlada. Como varias comprobaciones terminan por solicitud, la plataforma pregunta primero si la organización tiene algún flujo activo que escuche check.completed. Sin ningún oyente, no se crean trabajos de envío. La marca de referencia por ejecución evita que una nueva verificación vuelva a emitir las comprobaciones de una ejecución anterior.

Contexto de datos.

CampoTipoNotas
check.typestringTipo de comprobación, p. ej. "sanctions", "face_match", "ocr"
check.statusstring"passed" / "failed" / "error"
check.providerstring | nullProveedor que ejecutó la comprobación
check.scorenumber | nullPuntuación del proveedor, cuando aplique

Nivel superior: checkKind, kycRequestId.

{
  "organizationId": "org_01H...",
  "event": "check.completed",
  "kycRequestId": "req_01J...",
  "checkKind": "face_match",
  "context": {
    "check": { "type": "face_match", "status": "passed",
               "provider": "azure_face", "score": 0.93 }
  }
}

Ejemplo de uso en un flujo de trabajo. Reaccione a cualquier comprobación de sanciones que termine, sea cual sea el resultado (para un resultado claro de aprobado o no aprobado, prefiera los eventos específicos sanctions.* / face_match.* de más abajo):

trigger:
  event: check.completed
  subject: check
  scope: { mode: category, category: "sanctions" }

liveness.passed / liveness.failed

«Prueba de vida superada» / «Prueba de vida no superada» · sujeto check (liveness)

Cuándo se activa. Después de la orquestación, cuando la comprobación de vida ha guardado un booleano isLive inequívoco. passed cuando isLive es verdadero, failed cuando es falso. Una comprobación de vida con error o ambigua no emite ninguno de los dos.

Contexto de datos.

CampoTipoNotas
liveness.isLivebooleanSi el solicitante superó la prueba de vida
liveness.confidencenumber | nullConfianza de 0 a 1
{
  "organizationId": "org_01H...",
  "event": "liveness.failed",
  "kycRequestId": "req_01J...",
  "checkKind": "liveness",
  "context": { "liveness": { "isLive": false, "confidence": 0.31 } }
}

Ejemplo de uso en un flujo de trabajo. Escale ante un fallo de prueba de vida con baja confianza:

trigger:
  event: liveness.failed
  subject: check
  scope: { mode: all }
  filter: 'liveness.confidence < 0.5'

face_match.passed / face_match.failed

«Coincidencia facial superada» / «Coincidencia facial no superada» · sujeto check (face_match)

Cuándo se activa. Después de la orquestación, cuando la comprobación de coincidencia facial (selfi ↔ retrato del documento) ha guardado un booleano isMatch inequívoco. passed cuando isMatch es verdadero, failed cuando es falso. Una comprobación con error o ambigua no emite ninguno de los dos.

Contexto de datos.

CampoTipoNotas
faceMatch.isMatchbooleanSi el selfi coincidió con el rostro del documento
faceMatch.confidencenumber | nullConfianza de 0 a 1
{
  "organizationId": "org_01H...",
  "event": "face_match.failed",
  "kycRequestId": "req_01J...",
  "checkKind": "face_match",
  "context": { "faceMatch": { "isMatch": false, "confidence": 0.42 } }
}

Ejemplo de uso en un flujo de trabajo. Escale cualquier fallo de coincidencia facial para revisión manual:

trigger:
  event: face_match.failed
  subject: check
  scope: { mode: all }

sanctions.hit / sanctions.cleared

«Coincidencia en sanciones» / «Sin coincidencias en sanciones» · sujeto check (sanctions)

Cuándo se activa. Después de la orquestación, a partir del resultado guardado de sanciones o listas de vigilancia. hit cuando una coincidencia alcanza el umbral (o cuando hay cualquier coincidencia); cleared en caso contrario. Una coincidencia que corresponde específicamente a una persona expuesta políticamente también activa el evento dedicado pep.match, así que escuche ese evento cuando solo le interese la exposición PEP.

Contexto de datos.

CampoTipoNotas
sanctions.isMatchbooleanVerdadero en una coincidencia
sanctions.matchCountnumberNúmero de coincidencias en listas de vigilancia
sanctions.topScorenumber | nullPuntuación de la coincidencia más fuerte, de 0 a 1
sanctions.listsCheckedstring[]Listas revisadas (OFAC, OpenSanctions, …)
{
  "organizationId": "org_01H...",
  "event": "sanctions.hit",
  "kycRequestId": "req_01J...",
  "checkKind": "sanctions",
  "context": {
    "sanctions": { "isMatch": true, "matchCount": 2, "topScore": 0.91,
                   "listsChecked": ["OFAC", "OpenSanctions"] }
  }
}

Ejemplo de uso en un flujo de trabajo. Escale y notifique a cumplimiento normativo ante una coincidencia fuerte en sanciones:

trigger:
  event: sanctions.hit
  subject: check
  scope: { mode: all }
  filter: 'sanctions.topScore >= 0.85'

pep.match

«Se encuentra una coincidencia PEP» · sujeto check (sanctions)

Cuándo se activa. Después de la orquestación, cuando el resultado de sanciones o listas de vigilancia contiene al menos una coincidencia cuyo tipo de entidad es una persona expuesta políticamente. Utiliza la misma revisión que sanctions.*, pero solo se activa cuando hay una entrada PEP: una coincidencia simple en una lista de vigilancia sin entradas PEP no lo activa.

Contexto de datos.

CampoTipoNotas
pep.isMatchbooleanSiempre true (el evento solo se activa con una coincidencia PEP)
pep.matchCountnumberNúmero de coincidencias PEP
{
  "organizationId": "org_01H...",
  "event": "pep.match",
  "kycRequestId": "req_01J...",
  "checkKind": "sanctions",
  "context": { "pep": { "isMatch": true, "matchCount": 1 } }
}

Ejemplo de uso en un flujo de trabajo. Active automáticamente la Diligencia debida reforzada y derive el caso a cumplimiento normativo siempre que se detecte una PEP:

trigger:
  event: pep.match
  subject: check
  scope: { mode: all }

kyb.match_found / kyb.match_not_found

«Coincidencia KYB de empresa encontrada» / «No se encontró coincidencia KYB» · en toda la organización

Cuándo se activa. Solo para solicitudes de tipo BUSINESS, después de que la orquestación guarda el resultado de verificación del registro mercantil. verified → kyb.match_found; failed → kyb.match_not_found. Cualquier otro estado (pendiente o ausente) es ambiguo y no emite nada.

Contexto de datos.

CampoTipoNotas
kyb.statusstring"verified" o "failed"
{
  "organizationId": "org_01H...",
  "event": "kyb.match_not_found",
  "kycRequestId": "req_01J...",
  "context": { "kyb": { "status": "failed" } }
}

Ejemplo de uso en un flujo de trabajo. Solicite documentos de constitución adicionales cuando no se encuentre ninguna coincidencia en el registro.

kyb.ubo_completed

«Verificación de titulares reales (UBO) completada» · en toda la organización

Cuándo se activa. Solo para solicitudes de tipo BUSINESS, después de que la orquestación ha resuelto todos los registros de titulares reales (UBO) de la empresa (ninguno queda pending). Transporta el número de UBO revisados y cuántos fallaron, de modo que un flujo pueda ramificarse según el riesgo de los UBO sin volver a consultar. Solo se activa cuando la empresa tiene registros de UBO y la fila KYB se modificó en esta ejecución.

Contexto de datos.

CampoTipoNotas
kyb.uboCountnumberNúmero de titulares reales (UBO) revisados
kyb.failedUboCountnumberCuántos de esos UBO no superaron la verificación
{
  "organizationId": "org_01H...",
  "event": "kyb.ubo_completed",
  "kycRequestId": "req_01J...",
  "context": { "kyb": { "uboCount": 3, "failedUboCount": 1 } }
}

Ejemplo de uso en un flujo de trabajo. Escale cuando algún titular real no supere la revisión:

trigger:
  event: kyb.ubo_completed
  filter: 'kyb.failedUboCount > 0'

Revisión humana

reviewer.decision

«Un revisor toma una decisión manual» · en toda la organización

Cuándo se activa. Un revisor humano registra una decisión manual en el panel: aprobación única, cualquiera de las dos etapas de un flujo de dos revisores, o una acción en lote.

Contexto de datos.

CampoTipoNotas
decisionstringLa elección del revisor
reviewerIdstringId del revisor que decide
stagestring | undefined"first_approval" / "second_approval" en flujos de dos revisores
viastring | undefined"bulk" cuando forma parte de una acción en lote
{
  "organizationId": "org_01H...",
  "event": "reviewer.decision",
  "kycRequestId": "req_01J...",
  "context": { "decision": "approve", "reviewerId": "usr_01H...",
               "stage": "second_approval" }
}

Ejemplo de uso en un flujo de trabajo. Registre en el log de auditoría cada decisión manual y notifique al solicitante tras una aprobación final (la segunda).

reviewer.assigned

«Una solicitud se asigna a un revisor» · en toda la organización

Cuándo se activa. Una solicitud se reasigna a un nuevo responsable o revisor desde la acción de reasignar del panel. El responsable inicial que se define al crear la solicitud no activa este evento; solo lo hace una reasignación explícita, de modo que el evento marca un cambio real de enrutamiento.

Contexto de datos.

CampoTipoNotas
assignedUserIdstringEl nuevo responsable o revisor
previousUserIdstring | nullEl responsable anterior
teamIdstring | nullEl equipo al que pertenece ahora la solicitud
{
  "organizationId": "org_01H...",
  "event": "reviewer.assigned",
  "kycRequestId": "req_01J...",
  "context": { "assignedUserId": "usr_new...", "previousUserId": "usr_old...",
               "teamId": "team_01H..." }
}

Ejemplo de uso en un flujo de trabajo. Envíe al revisor recién asignado una notificación con un enlace directo al caso.

reviewer.comment_added

«Un revisor añade un comentario» · en toda la organización

Cuándo se activa. Un revisor publica un comentario en un caso (el flujo de comentarios del caso). El cuerpo del comentario no se incluye intencionadamente en el contexto del evento, porque puede contener notas sensibles; por eso el evento solo transporta metadatos.

Contexto de datos.

CampoTipoNotas
commentIdstringId del comentario creado
authorIdstringEl revisor que comenta
authorNamestringNombre visible del autor
{
  "organizationId": "org_01H...",
  "event": "reviewer.comment_added",
  "kycRequestId": "req_01J...",
  "context": { "commentId": "cmt_01J...", "authorId": "usr_01H...",
               "authorName": "Jordan Lee" }
}

Ejemplo de uso en un flujo de trabajo. Reproduzca los nuevos comentarios del caso en un canal de Slack para el equipo de cumplimiento normativo.

edd.triggered

«Se activa la diligencia debida reforzada» · sujeto applicant

Cuándo se activa. Se inicia la Diligencia debida reforzada (EDD) para una solicitud (actualmente, la acción manual de EDD).

Contexto de datos.

CampoTipoNotas
kindstring"edd"
{
  "organizationId": "org_01H...",
  "event": "edd.triggered",
  "kycRequestId": "req_01J...",
  "context": { "kind": "edd" }
}

Ejemplo de uso en un flujo de trabajo. Al iniciarse la EDD, solicite documentos sobre el origen de los fondos y asigne el caso al departamento de cumplimiento normativo.

Riesgo / puntuación

Ambos eventos de puntuación se activan desde la ruta de decisión una vez que el motor de puntuación ha registrado una puntuación de confianza para la ejecución (una solicitud que se finaliza sin puntuación, por ejemplo cancelada antes de puntuarse, no emite ninguno de los dos). Transportan el contexto completo de decisión, así que overallScore, decision, status y applicant.* están disponibles para los filtros.

score.updated

«Se actualiza la puntuación de confianza» · sujeto applicant

Cuándo se activa. En cada finalización con puntuación (nueva o recalculada), incluido un nuevo análisis que recalcula la puntuación. Úselo para reaccionar a la nueva puntuación de confianza, sea cual sea el veredicto.

Contexto de datos. El contexto de decisión compartido (overallScore, decision, status, applicant.country, applicant.type).

{
  "organizationId": "org_01H...",
  "event": "score.updated",
  "kycRequestId": "req_01J...",
  "applicantId": "app_01J...",
  "context": {
    "status": "APPROVED",
    "decision": "approve",
    "overallScore": 72,
    "applicant": { "country": "CA", "type": "INDIVIDUAL" }
  }
}

Ejemplo de uso en un flujo de trabajo. Etiquete cualquier solicitud cuya puntuación caiga en una franja de revisión manual:

trigger:
  event: score.updated
  subject: applicant
  scope: { mode: all }
  filter: 'overallScore >= 50 AND overallScore < 70'

score.threshold

«La puntuación de confianza cruza un umbral» · sujeto applicant

Cuándo se activa. Mismo punto de emisión que score.updated, pero el despachador lo restringe mediante los límites scoreMin / scoreMax propios del disparador (lee context.overallScore). Configure los límites en el disparador y el flujo solo se ejecutará cuando la puntuación caiga dentro de ellos: es una comodidad frente a escribir a mano un filtro de overallScore.

Contexto de datos. Idéntico a score.updated.

{
  "organizationId": "org_01H...",
  "event": "score.threshold",
  "kycRequestId": "req_01J...",
  "applicantId": "app_01J...",
  "context": {
    "status": "REJECTED",
    "decision": "reject",
    "overallScore": 38,
    "applicant": { "country": "US", "type": "INDIVIDUAL" }
  }
}

Ejemplo de uso en un flujo de trabajo. Ejecute el flujo solo para puntuaciones bajas, estableciendo un límite superior en el disparador:

trigger:
  event: score.threshold
  subject: applicant
  scope: { mode: all }
  scoreMax: 50          # fire only when overallScore <= 50

scoreMin / scoreMax son inclusivos. Configure uno o ambos. Si no se establece ninguno, score.threshold se comporta igual que score.updated.

Tiempo / monitorización

schedule

«Según una programación» · en toda la organización, no vinculado a una solicitud

Cuándo se activa. Según la expresión cron propia del flujo de trabajo (se define en el campo schedule del disparador). La ejecución es de nivel organización (kycRequestId es null), así que úselo para trabajo por lotes o de mantenimiento, no para lógica por solicitante.

El cron es una expresión estándar de 5 campos (minuto hora día mes día-de-la-semana) evaluada en UTC y no puede ejecutarse con más frecuencia que cada 5 minutos: la publicación rechaza * * * * * o */2 * * * *.

Contexto de datos.

CampoTipoNotas
firedAtstringHora de activación en ISO-8601
cronstringLa expresión cron que se activó
{
  "organizationId": "org_01H...",
  "event": "schedule",
  "kycRequestId": null,
  "context": { "firedAt": "2026-05-31T09:00:00.000Z", "cron": "0 9 * * 1" }
}

Ejemplo de uso en un flujo de trabajo. Todos los lunes a las 09:00, genere un resumen semanal de verificaciones y publíquelo en Slack:

trigger:
  event: schedule
  schedule: "0 9 * * 1"

verification.expired

«Caduca una verificación» · sujeto applicant

Cuándo se activa. Desde el cron de caducidad, cuando una solicitud supera su ventana de caducidad.

Contexto de datos.

CampoTipoNotas
statusstring"EXPIRED"
{
  "organizationId": "org_01H...",
  "event": "verification.expired",
  "kycRequestId": "req_01J...",
  "context": { "status": "EXPIRED" }
}

Ejemplo de uso en un flujo de trabajo. Envíe al solicitante un enlace nuevo por correo y notifique al responsable cuando caduque una verificación.

monitoring.alert

«Se activa una alerta de monitorización» · en toda la organización

Cuándo se activa. Desde la revisión continua de sanciones: cuando un sujeto previamente despejado coincide ahora con una lista de vigilancia (monitorización continua).

Contexto de datos.

CampoTipoNotas
alertTypestringEl tipo de alerta de monitorización
severitystringGravedad de la alerta
topScorenumber | nullPuntuación de la nueva coincidencia más fuerte
{
  "organizationId": "org_01H...",
  "event": "monitoring.alert",
  "kycRequestId": "req_01J...",
  "context": { "alertType": "sanctions_rescreen", "severity": "high",
               "topScore": 0.88 }
}

Ejemplo de uso en un flujo de trabajo. Ante una alerta de monitorización de gravedad alta, escale la solicitud y abra un caso de cumplimiento normativo.

Externo / integración

inbound.webhook

«Llega un webhook entrante» · en toda la organización, carga útil arbitraria

Cuándo se activa. Su sistema envía un POST al endpoint de webhooks entrantes del flujo de trabajo. El context es el JSON que envíe, así que usted controla por completo su forma. No está vinculado a una solicitud, salvo que su carga útil aporte un id que el flujo utilice.

Endpoint. POST /api/v1/kyc/workflows/inbound, autenticado con una clave API que tenga el scope workflows:trigger. Ningún scope kyc:* lo incluye: marque Flujos de automatización → Activar al crear o editar la clave. Su plan debe incluir los flujos de automatización (si no, 402). Envíe context (su objeto JSON, máximo 64 KB) y, opcionalmente, eventId (su propio id: reintentar con el mismo eventId nunca inicia dos veces el mismo flujo). La llamada responde 202 y las ejecuciones se realizan en segundo plano. Una clave restringida a ciertos flujos solo inicia esos flujos.

curl -X POST https://app.certivant.com/api/v1/kyc/workflows/inbound \
  -H "Authorization: Bearer ck_live_..." \
  -H "Content-Type: application/json" \
  -d '{"eventId":"crm-9281-1","context":{"externalRef":"user-9281","tier":"vip"}}'

Contexto de datos. Arbitrario: el cuerpo JSON enviado, tal cual.

{
  "organizationId": "org_01H...",
  "event": "inbound.webhook",
  "context": { "externalRef": "user-9281", "tier": "vip", "source": "crm" }
}

Ejemplo de uso en un flujo de trabajo. Deje que su CRM inicie un lote de nueva verificación para los clientes marcados; filtre por un campo que haya enviado:

trigger:
  event: inbound.webhook
  filter: 'tier = "vip"'

conference.completed

«Termina una conferencia en vivo» · en toda la organización

Cuándo se activa. Termina una conferencia web de verificación de identidad en vivo (Live ID) y el anfitrión (notario o revisor) registra una decisión. Se emite una sola vez, protegido para que una decisión simultánea no lo active dos veces.

Contexto de datos.

CampoTipoNotas
decisionstringLa decisión registrada por el anfitrión
statusstringEl estado resultante de la solicitud
{
  "organizationId": "org_01H...",
  "event": "conference.completed",
  "kycRequestId": "req_01J...",
  "context": { "decision": "approve", "status": "APPROVED" }
}

Ejemplo de uso en un flujo de trabajo. Después de una conferencia, genere el Certificado de Verificación firmado y envíeselo por correo al solicitante.


Filtrado con el DSL

El filter del disparador (y cada nodo Condición) usa la gramática de expresiones de la política: un único predicado booleano, sin llamadas a funciones:

  • Campos: el contexto aplanado del evento (los campos que aparecen en la tabla Contexto de datos de cada disparador).
  • Operadores: = != > >= < <=, IN / NOT IN.
  • Combinadores: AND OR NOT, con agrupación ( ).
  • Literales: números, "cadenas", true / false / null, y listas [...].

El operador de igualdad es un único = (no ==). Los campos ausentes son seguros ante fallos: un campo ausente se evalúa como falso en lugar de lanzar un error, así que un filtro que haga referencia a sanctions.topScore en un evento que no sea de sanciones, sencillamente, no coincide.

Qué campos se rellenan depende del evento: un evento sanctions.* transporta sanctions.*, un evento liveness.* transporta liveness.*, y los eventos de decisión transportan overallScore / decision / status / applicant.*.

CampoTipoDisponible en
overallScorenumbereventos de decisión / estado
decisionstringeventos de decisión / estado
statusstringeventos de estado / decisión / ciclo de vida
applicant.countrystringeventos de entrada / decisión
applicant.typestringeventos de entrada / decisión
document.typestringdocument.*
document.sidestringdocument.*
liveness.isLivebooleanliveness.*
liveness.confidencenumberliveness.*
faceMatch.isMatchbooleanface_match.*
faceMatch.confidencenumberface_match.*
sanctions.isMatchbooleansanctions.*
sanctions.topScorenumbersanctions.*
sanctions.matchCountnumbersanctions.*
pep.isMatchbooleanpep.match
pep.matchCountnumberpep.match
kyb.statusstringkyb.match_*
kyb.uboCountnumberkyb.ubo_completed
kyb.failedUboCountnumberkyb.ubo_completed
reviewerIdstringreviewer.decision

Ejemplos de predicados:

overallScore >= 75
decision = "reject"
applicant.country IN ["CA","US"]
document.type = "passport"
sanctions.isMatch = true AND sanctions.topScore >= 0.85
overallScore < 50 OR sanctions.isMatch = true

En el editor, pulse Ctrl+Space en cualquier campo de expresión para obtener autocompletado según el contexto y una ventana de ayuda con campos, operadores y ejemplos listos para usar.


Tabla comparativa

DisparadorCategoríaSujetoVinculado a la solicitudFrecuenciaNotas
form.submittedEntradaformsípor envíoSolo formularios alojados
request.createdEntradaapplicantsípor solicitudAntes de ejecutar cualquier comprobación
request.status_changedCiclo de vidaapplicantsípor cambio de estadoGenérico; se combina con los eventos específicos
request.approvedCiclo de vidaapplicantsípor finalización
request.rejectedCiclo de vidaapplicantsípor finalización
request.escalatedCiclo de vidaapplicantsípor finalización
request.resubmittedCiclo de vidaapplicantsípor reenvío
request.documents_readyCiclo de vidaapplicantsípor completar la listaTodos los documentos requeridos cargados
document.uploadedDocumentosdocumentsípor documento
document.failedDocumentosdocumentsípor documento fallido
check.completedSeñaleschecksípor comprobación (alta)Protegido; una por comprobación
liveness.passedSeñaleschecksípor solicitudSolo inequívoco
liveness.failedSeñaleschecksípor solicitudSolo inequívoco
face_match.passedSeñaleschecksípor solicitudSolo inequívoco
face_match.failedSeñaleschecksípor solicitudSolo inequívoco
sanctions.hitSeñaleschecksípor solicitudCoincidencia en lista de vigilancia
sanctions.clearedSeñaleschecksípor solicitud
pep.matchSeñaleschecksípor solicitudSolo entradas PEP
kyb.match_foundSeñales—sípor solicitud de empresaSolo BUSINESS
kyb.match_not_foundSeñales—sípor solicitud de empresaSolo BUSINESS
kyb.ubo_completedSeñales—sípor solicitud de empresaSolo BUSINESS; UBO resueltos
reviewer.decisionRevisión—sípor decisión manualEtapas + lote
reviewer.assignedRevisión—sípor reasignaciónSolo reasignación explícita
reviewer.comment_addedRevisión—sípor comentarioEl cuerpo no está en el contexto
edd.triggeredRevisiónapplicantsípor inicio de EDD
score.updatedRiesgoapplicantsípor (re)puntuaciónSe activa junto con la decisión
score.thresholdRiesgoapplicantsípor (re)puntuaciónRestringido por scoreMin/scoreMax
scheduleTiempo—nocronEjecución a nivel de organización
verification.expiredTiempoapplicantsípor caducidadDesde el cron de caducidad
monitoring.alertMonitorización—sípor alertaRevisión continua
inbound.webhookExterno—opcionalpor POSTCarga útil arbitraria
conference.completedExterno—sípor conferenciaSesiones de identidad en vivo

Disparadores previstos

Algunos disparadores solicitados con frecuencia no aparecen en el selector, porque la plataforma todavía no tiene ninguna señal que los emita; incluirlos como opciones seleccionables crearía disparadores que nunca se activan. Se enumeran aquí con la alternativa que sí funciona, para que la carencia sea explícita. (Todo lo que aparece en el selector, en cambio, sí se emite, incluidos score.threshold, la coincidencia facial, PEP, UBO y los eventos de revisor, que se conectaron en esta versión.)

Disparador solicitadoPor qué no se emiteQué usar en su lugar
Fraude detectadoTodavía no existe un tipo de comprobación independiente de análisis forense o de autenticidad (el análisis forense es actualmente un factor de puntuación de riesgo, no una comprobación discreta que emita un resultado).Ramifíquese según document.failed, o según los eventos de decisión / score.threshold para el riesgo resultante.
Datos del registro mercantil actualizadosKYB es una verificación única; no hay ningún trabajo continuo de nueva consulta o monitorización del registro desde el que activarse.Repita la verificación mediante un schedule y ramifíquese según kyb.match_* / kyb.ubo_completed.
Clic en un enlace de correo o SMSNo existe infraestructura de seguimiento de aperturas de correo ni de clics en enlaces.Use form.submitted / document.uploaded como señales concretas de que el solicitante actuó.

Son elementos genuinos de la hoja de ruta, no carencias de la documentación: la enumeración de disparadores del motor es la única fuente de verdad, y esta tabla refleja lo que todavía no contiene.


Buenas prácticas

  • Defina siempre un sujeto y un alcance. Un disparador sin sujeto es de toda la organización y se activará con cada objeto coincidente de la organización. Prefiera un alcance specific o category; reserve all para automatizaciones verdaderamente de toda la organización.
  • Escuche eventos específicos, no el genérico, siempre que sea posible. Use liveness.failed / sanctions.hit en lugar de analizar check.completed, y un único evento de decisión en lugar de request.status_changed, salvo que necesite específicamente un estado sin evento dedicado.
  • No escuche a la vez los eventos de estado específicos y el genérico. request.approved y request.status_changed se activan ambos con la aprobación; vincular dos flujos (o dos disparadores) al mismo resultado duplica la ejecución de los efectos secundarios.
  • Trate el contexto como una instantánea. El contexto del evento se captura en el momento del envío. Si su flujo espera (un nodo wait, una demora larga), vuelva a leer el estado en vivo dentro de una Acción en lugar de confiar en un contexto obsoleto para una decisión crítica.
  • Mantenga los flujos de check.completed ligeros. Es el disparador de mayor frecuencia; limítelo a una categoría y filtre pronto para no multiplicar la ejecución por cada comprobación de cada solicitud.
  • Atención a los alcances huérfanos. Eliminar un formulario o un solicitante al que hace referencia un alcance specific deja el disparador en reposo (no se activará y muestra una insignia «Requiere reasignación»). Vuelva a vincularlo a un objeto activo para reactivarlo.
  • Los flujos escalan, nunca deciden. Si el resultado de un disparador debe cambiar el veredicto, pase por escalate hacia la revisión humana: intencionadamente no existe una acción de aprobar o rechazar.
  • Espere una ejecución al menos una vez y de forma eventual. El envío es asíncrono y sin espera de respuesta; haga que los efectos secundarios de las Acciones sean idempotentes donde importe (por ejemplo, incluya una referencia externa para que un nuevo envío no duplique registros posteriores).

Preguntas frecuentes

¿Puede un flujo aprobar o rechazar una verificación? No. El conjunto de Acciones no tiene approve ni reject. El control más fuerte sobre el resultado es escalate, que traslada la solicitud a revisión humana mediante la máquina de estados existente, con bloqueo optimista y auditada. El veredicto corresponde a la política de decisión y a los revisores humanos.

Se activaron una decisión específica y request.status_changed; ¿es un error? No. El genérico request.status_changed siempre se emite junto con el evento específico para que pueda reaccionar a estados que no tienen un evento dedicado. Escuche uno u otro, no ambos.

¿Por qué no se ejecutó mi flujo de check.completed? La multiplicación por comprobación está protegida: solo envía cuando un flujo activo escucha check.completed. Confirme que el flujo está activo y que su alcance y su filtro coinciden con la comprobación. Tenga en cuenta también que las comprobaciones con error y sin un dato booleano utilizable no emiten las señales específicas liveness.* / sanctions.*.

¿Por qué no se activa mi disparador score.threshold? Se activa en cada finalización con puntuación y después el despachador lo restringe con los límites scoreMin / scoreMax del disparador. Si nunca se ejecuta, compruebe que la puntuación realmente cae dentro de sus límites y que la solicitud llegó a un resultado puntuado (una solicitud cancelada antes de puntuarse no emite ningún evento de puntuación). Sin límites configurados, se comporta como score.updated.

¿Cuál es la diferencia entre score.updated y score.threshold? El mismo punto de emisión y el mismo contexto. score.updated siempre se ejecuta; score.threshold solo se ejecuta cuando overallScore cae dentro de los límites scoreMin / scoreMax del disparador: una comodidad integrada para que no tenga que escribir usted el filtro de overallScore.

¿Cómo activo un disparador según un resultado de coincidencia facial o PEP? Use los eventos dedicados face_match.passed / face_match.failed y pep.match. Un resultado PEP también activa sanctions.hit (la detección PEP se apoya en la revisión de listas de vigilancia), así que escuche pep.match cuando solo le interese la exposición PEP.

¿Eliminar un formulario desactiva sus flujos? Un flujo cuyo disparador tiene un alcance specific limitado únicamente a ese formulario queda huérfano: pasa a estar en reposo (no se activará) y muestra una insignia «Requiere reasignación». Vuelva a vincularlo a un objeto activo para reactivarlo. Nunca recurre en silencio a activarse en toda la organización.

¿El envío es síncrono con mi llamada a la API? No. El envío es sin espera de respuesta y se ejecuta fuera de banda. La acción original (carga, cambio de estado) se completa independientemente de si el envío o la ejecución del flujo tienen éxito.

¿Puede un flujo activar otro? Sí: la acción trigger_workflow ejecuta directamente un flujo guardado elegido, con una profundidad máxima de encadenamiento de 5 para evitar bucles.


Apéndice

Glosario

  • Disparador: el único nodo de entrada que declara el evento, el sujeto, el alcance y el filtro que inician un flujo.
  • Evento: un WorkflowTriggerEvent que la plataforma emite en un punto real del ciclo de vida de la verificación.
  • Sujeto: el tipo de objeto al que se vincula un disparador: form, applicant, document o check.
  • Alcance: cómo se restringe el sujeto: specific (ids), category o all.
  • Contexto: los hechos específicos del evento capturados en el envío y que leen los filtros y las plantillas de Acción.
  • Filtro: un predicado DSL opcional que restringe aún más qué eventos coinciden.
  • Disparador huérfano: un disparador con alcance specific cuyos objetos se eliminaron; permanece en reposo hasta que se vuelva a vincular.
  • Ejecución: un KycWorkflowRun: una ejecución de un flujo, con un log de solo anexado.

Enumeración completa de eventos

type WorkflowTriggerEvent =
  | "form.submitted" | "request.created"
  | "request.status_changed" | "request.approved" | "request.rejected"
  | "request.escalated" | "request.resubmitted" | "request.documents_ready"
  | "document.uploaded" | "document.failed" | "check.completed"
  | "liveness.passed" | "liveness.failed"
  | "face_match.passed" | "face_match.failed"
  | "sanctions.hit" | "sanctions.cleared" | "pep.match"
  | "kyb.match_found" | "kyb.match_not_found" | "kyb.ubo_completed"
  | "reviewer.decision" | "reviewer.assigned" | "reviewer.comment_added"
  | "score.threshold" | "score.updated" | "edd.triggered"
  | "monitoring.alert" | "verification.expired" | "schedule"
  | "inbound.webhook" | "conference.completed";

Ciclo de vida de un envío

1. Ocurre algo                  (envío de formulario, termina una comprobación, cron, webhook…)
2. El emisor llama a            enqueueWorkflowDispatch({ event, …, context })
3. workflow.worker lo recoge    → dispatchWorkflowEvent(input)
4. Buscar flujos activos        trigger.event === input.event
5. Restringir por sujeto        ids específicos / category / all  (los huérfanos quedan excluidos)
6. Evaluar el filtro (DSL)      sobre input.context  (campos ausentes = false)
7. Crear KycWorkflowRun         contexto guardado en la primera entrada del log
8. Ejecutar los nodos           Condición → Acción → Espera → … → Fin

Ejemplo de extremo a extremo

Una automatización de incorporación completa vinculada a un único formulario KYC:

trigger:
  event: form.submitted
  subject: form
  scope: { mode: specific, ids: ["form_onboarding_01H..."] }

# graph (conceptual):
#   ▸ Trigger: form.submitted
#   → Action: email applicant "We received your submission"
#   → Wait: until check.completed (or 24h timeout)
#   → Condition: sanctions.isMatch = true
#        true  → Action: escalate  +  Action: notify compliance (Slack)
#        false → Condition: overallScore >= 80
#                     true  → Action: generate_pdf_report → email applicant
#                     false → Action: assign_department "manual-review"
#   → End

Esto nunca establece el veredicto: el único paso que afecta al resultado es escalate, que deriva a revisión humana. Todo lo demás son notificaciones, recopilación de datos e informes alrededor de la decisión que son responsabilidad de la política y de los revisores.