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
contextdel 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:
| Campo | Tipo | Presente cuando |
|---|---|---|
organizationId | string | siempre |
event | WorkflowTriggerEvent | siempre |
kycRequestId | string | null | el evento concierne a una verificación concreta (la mayoría de eventos) |
formId | string | null | form.submitted |
formKind | "kyc" | "kyb" | null | form.submitted |
applicantId | string | null | eventos con alcance de solicitante |
documentType | string | null | document.* |
checkKind | string | null | check.completed, liveness.*, sanctions.* |
context | object | hechos específicos del evento (véase cada disparador más abajo) |
workflowId | string | solo en una invocación directa de trigger_workflow |
chainDepth | number | profundidad 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.
| Sujeto | Eventos válidos | Opciones de scope.mode | Significado de category |
|---|---|---|---|
form | form.submitted | specific (ids de formulario) · category · all | "kyc" o "kyb" |
applicant | request.*, score.threshold, edd.triggered, verification.expired | specific (ids de solicitante) · all | — |
document | document.uploaded, document.failed | specific · category · all | un tipo de documento, p. ej. "passport" |
check | check.completed, liveness.*, sanctions.* | specific · category · all | un 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 losidsindicados (por ejemplo, activarse solo para este formulario de incorporación). Si todos los ids de un alcancespecificapuntan 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ía | Disparadores |
|---|---|
| Entrada | form.submitted, request.created |
| Ciclo de vida / estado | request.status_changed, request.approved, request.rejected, request.escalated, request.resubmitted, request.documents_ready |
| Documentos | document.uploaded, document.failed |
| Señales de verificación | 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 |
| Revisión humana | reviewer.decision, reviewer.assigned, reviewer.comment_added, edd.triggered |
| Riesgo / puntuación | score.threshold, score.updated |
| Tiempo / monitorización | schedule, verification.expired, monitoring.alert |
| Externo / integración | inbound.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.
| Campo | Tipo | Notas |
|---|---|---|
type | string | Tipo 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.
| Campo | Tipo | Notas |
|---|---|---|
applicant.country | string | null | ISO-3166 alpha-2, a partir de la nacionalidad del solicitante |
applicant.type | string | "INDIVIDUAL" o "BUSINESS" |
type | string | Tipo 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):
| Campo | Tipo | Notas |
|---|---|---|
status | string | Nuevo estado, p. ej. "APPROVED", "REJECTED", "ESCALATED", "PENDING_SECOND_REVIEW" |
decision | string | null | Veredicto del motor de puntuación: "approve" / "reject" / "escalate" |
overallScore | number | null | Puntuación de confianza de 0 a 100 (más alta = menor riesgo) |
applicant.country | string | null | ISO-3166 alpha-2 |
applicant.type | string | INDIVIDUAL / 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.
| Campo | Tipo | Notas |
|---|---|---|
status | string | "PENDING_UPLOAD" |
kind | string | "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.
| Campo | Tipo | Notas |
|---|---|---|
status | string | "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.
| Campo | Tipo | Notas |
|---|---|---|
document.type | string | p. ej. "passport", "id_card", "drivers_license" |
document.side | string | 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.
| Campo | Tipo | Notas |
|---|---|---|
check.type | string | Tipo de comprobación, p. ej. "sanctions", "face_match", "ocr" |
check.status | string | "passed" / "failed" / "error" |
check.provider | string | null | Proveedor que ejecutó la comprobación |
check.score | number | null | Puntuació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.
| Campo | Tipo | Notas |
|---|---|---|
liveness.isLive | boolean | Si el solicitante superó la prueba de vida |
liveness.confidence | number | null | Confianza 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.
| Campo | Tipo | Notas |
|---|---|---|
faceMatch.isMatch | boolean | Si el selfi coincidió con el rostro del documento |
faceMatch.confidence | number | null | Confianza 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.
| Campo | Tipo | Notas |
|---|---|---|
sanctions.isMatch | boolean | Verdadero en una coincidencia |
sanctions.matchCount | number | Número de coincidencias en listas de vigilancia |
sanctions.topScore | number | null | Puntuación de la coincidencia más fuerte, de 0 a 1 |
sanctions.listsChecked | string[] | 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.
| Campo | Tipo | Notas |
|---|---|---|
pep.isMatch | boolean | Siempre true (el evento solo se activa con una coincidencia PEP) |
pep.matchCount | number | Nú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.
| Campo | Tipo | Notas |
|---|---|---|
kyb.status | string | "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.
| Campo | Tipo | Notas |
|---|---|---|
kyb.uboCount | number | Número de titulares reales (UBO) revisados |
kyb.failedUboCount | number | Cuá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.
| Campo | Tipo | Notas |
|---|---|---|
decision | string | La elección del revisor |
reviewerId | string | Id del revisor que decide |
stage | string | undefined | "first_approval" / "second_approval" en flujos de dos revisores |
via | string | 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.
| Campo | Tipo | Notas |
|---|---|---|
assignedUserId | string | El nuevo responsable o revisor |
previousUserId | string | null | El responsable anterior |
teamId | string | null | El 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.
| Campo | Tipo | Notas |
|---|---|---|
commentId | string | Id del comentario creado |
authorId | string | El revisor que comenta |
authorName | string | Nombre 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.
| Campo | Tipo | Notas |
|---|---|---|
kind | string | "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/scoreMaxson inclusivos. Configure uno o ambos. Si no se establece ninguno,score.thresholdse comporta igual quescore.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.
| Campo | Tipo | Notas |
|---|---|---|
firedAt | string | Hora de activación en ISO-8601 |
cron | string | La 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.
| Campo | Tipo | Notas |
|---|---|---|
status | string | "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.
| Campo | Tipo | Notas |
|---|---|---|
alertType | string | El tipo de alerta de monitorización |
severity | string | Gravedad de la alerta |
topScore | number | null | Puntuació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.
| Campo | Tipo | Notas |
|---|---|---|
decision | string | La decisión registrada por el anfitrión |
status | string | El 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:
ANDORNOT, 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 asanctions.topScoreen 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.*.
| Campo | Tipo | Disponible en |
|---|---|---|
overallScore | number | eventos de decisión / estado |
decision | string | eventos de decisión / estado |
status | string | eventos de estado / decisión / ciclo de vida |
applicant.country | string | eventos de entrada / decisión |
applicant.type | string | eventos de entrada / decisión |
document.type | string | document.* |
document.side | string | document.* |
liveness.isLive | boolean | liveness.* |
liveness.confidence | number | liveness.* |
faceMatch.isMatch | boolean | face_match.* |
faceMatch.confidence | number | face_match.* |
sanctions.isMatch | boolean | sanctions.* |
sanctions.topScore | number | sanctions.* |
sanctions.matchCount | number | sanctions.* |
pep.isMatch | boolean | pep.match |
pep.matchCount | number | pep.match |
kyb.status | string | kyb.match_* |
kyb.uboCount | number | kyb.ubo_completed |
kyb.failedUboCount | number | kyb.ubo_completed |
reviewerId | string | reviewer.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
| Disparador | Categoría | Sujeto | Vinculado a la solicitud | Frecuencia | Notas |
|---|---|---|---|---|---|
form.submitted | Entrada | form | sí | por envío | Solo formularios alojados |
request.created | Entrada | applicant | sí | por solicitud | Antes de ejecutar cualquier comprobación |
request.status_changed | Ciclo de vida | applicant | sí | por cambio de estado | Genérico; se combina con los eventos específicos |
request.approved | Ciclo de vida | applicant | sí | por finalización | |
request.rejected | Ciclo de vida | applicant | sí | por finalización | |
request.escalated | Ciclo de vida | applicant | sí | por finalización | |
request.resubmitted | Ciclo de vida | applicant | sí | por reenvío | |
request.documents_ready | Ciclo de vida | applicant | sí | por completar la lista | Todos los documentos requeridos cargados |
document.uploaded | Documentos | document | sí | por documento | |
document.failed | Documentos | document | sí | por documento fallido | |
check.completed | Señales | check | sí | por comprobación (alta) | Protegido; una por comprobación |
liveness.passed | Señales | check | sí | por solicitud | Solo inequívoco |
liveness.failed | Señales | check | sí | por solicitud | Solo inequívoco |
face_match.passed | Señales | check | sí | por solicitud | Solo inequívoco |
face_match.failed | Señales | check | sí | por solicitud | Solo inequívoco |
sanctions.hit | Señales | check | sí | por solicitud | Coincidencia en lista de vigilancia |
sanctions.cleared | Señales | check | sí | por solicitud | |
pep.match | Señales | check | sí | por solicitud | Solo entradas PEP |
kyb.match_found | Señales | — | sí | por solicitud de empresa | Solo BUSINESS |
kyb.match_not_found | Señales | — | sí | por solicitud de empresa | Solo BUSINESS |
kyb.ubo_completed | Señales | — | sí | por solicitud de empresa | Solo BUSINESS; UBO resueltos |
reviewer.decision | Revisión | — | sí | por decisión manual | Etapas + lote |
reviewer.assigned | Revisión | — | sí | por reasignación | Solo reasignación explícita |
reviewer.comment_added | Revisión | — | sí | por comentario | El cuerpo no está en el contexto |
edd.triggered | Revisión | applicant | sí | por inicio de EDD | |
score.updated | Riesgo | applicant | sí | por (re)puntuación | Se activa junto con la decisión |
score.threshold | Riesgo | applicant | sí | por (re)puntuación | Restringido por scoreMin/scoreMax |
schedule | Tiempo | — | no | cron | Ejecución a nivel de organización |
verification.expired | Tiempo | applicant | sí | por caducidad | Desde el cron de caducidad |
monitoring.alert | Monitorización | — | sí | por alerta | Revisión continua |
inbound.webhook | Externo | — | opcional | por POST | Carga útil arbitraria |
conference.completed | Externo | — | sí | por conferencia | Sesiones 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 solicitado | Por qué no se emite | Qué usar en su lugar |
|---|---|---|
| Fraude detectado | Todaví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 actualizados | KYB 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 SMS | No 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
specificocategory; reserveallpara automatizaciones verdaderamente de toda la organización. - Escuche eventos específicos, no el genérico, siempre que sea posible. Use
liveness.failed/sanctions.hiten lugar de analizarcheck.completed, y un único evento de decisión en lugar derequest.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.approvedyrequest.status_changedse 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.completedligeros. 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
specificdeja 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
escalatehacia 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
WorkflowTriggerEventque 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,documentocheck. - Alcance: cómo se restringe el sujeto:
specific(ids),categoryoall. - 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
specificcuyos objetos se eliminaron; permanece en reposo hasta que se vuelva a vincular. - Ejecución: un
KycWorkflowRun: una ejecución de un flujo, con unlogde 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.
Flujos de trabajo de automatización
Flujos de trabajo ejecutables activados por eventos que dirigen, notifican y orquestan una verificación, sin establecer nunca el veredicto.
SDK web
Muestre una verificación de Certivant dentro de su propio sitio web con una sola etiqueta de script: en modal o en línea, con eventos ready, step, complete, cancel y error, comprobaciones estrictas de origen y soporte de cámara.