Verificación

Configuración de flujos

Pasos, parámetros reales y cómo funcionan las reglas condicionales.

Un proyecto tiene una configuración de flujo: qué pasos corre, en qué orden, con qué parámetros y qué hacer cuando algo falla. Se arma desde el dashboard; esta página documenta el modelo que hay abajo, para que sepas exactamente qué significa cada opción.

Versionado y congelamiento

Cada vez que publicás, se crea una versión (cfg_00000003) que queda inmutable. Cada sesión se ata a la versión activa en su modo al momento de crearse.

Consecuencias que conviene tener presentes:

  • cambiar el flujo no afecta a las sesiones en curso ni a las ya resueltas;
  • el costo en tokens se congela con la versión: una sesión se cobra al precio vigente cuando se publicó ese flujo;
  • las opciones de payload de webhook también se leen de la versión congelada de esa sesión, no de la configuración actual del proyecto;
  • un proyecto tiene punteros independientes para test y para live: podés probar una versión nueva sin tocar producción.

Estructura de una configuración

CampoTipoRequeridoDescripción
namestringNombre del flujo, 1 a 120 caracteres.
stepsarrayDefinición de los pasos disponibles. Cada uno con id kebab-case único, tipo y parámetros.
flow.sequencestring[]Orden de ejecución. Al menos un paso.
flow.rulesarraynoReglas condicionales. Ver más abajo.
expiration.linkTtlHoursnumbernoVigencia por defecto del link: de 0.25 a 168 horas. Por defecto 24. Se puede pisar por sesión con expires_in_hours.
expiration.sessionIdleMinutesintegernoInactividad tolerada dentro del flujo antes de darlo por abandonado: de 1 a 1440 minutos. Por defecto 30.
review.enabledbooleannoSi los casos ambiguos van a revisión manual. Por defecto activado.
review.slaHoursnumbernoObjetivo de resolución de la revisión: de 1 a 720 horas. Por defecto 24.
webhookEventsstring[]noQué eventos dispara este proyecto. Vacío significa que no dispara ninguno.
webhookPayloadobjectnoQué datos adicionales viajan en los webhooks. Todo desactivado por defecto.
retentionDaysintegernoRetención de la evidencia: de 30 a 1825 días. Por defecto 1825 (5 años).
brandingobjectnoLogo, color primario, nombre de la app, idioma (por defecto es-AR) y textos personalizados.
redirectsobjectnoURLs a las que mandar a la persona al terminar: onApproved, onRejected, onExpired.

webhookPayload

Cuatro interruptores, todos apagados por defecto. Controlan qué sale en los eventos — ver Eventos y payload.

CampoTipoHabilitaDescripción
documentDatabooleanresult.documentDatos extraídos del documento.
verifiedChannelsbooleanresult.verified_channelsEmail y teléfono verificados por OTP.
formDatabooleanresult.form_dataRespuestas del formulario.
evidenceLinksbooleanevidence_linksLinks firmados a las imágenes capturadas, con 5 minutos de vida.

Parámetros de cada paso

document_ar

CampoTipoPor defectoDescripción
acceptedDocsstring[]Al menos uno de dni_card, passport, driver_license.
allowScreenCapturebooleanfalseSi se acepta la foto de una pantalla. Activarlo degrada la detección de pantalla de rechazo duro a señal blanda.
rejectExpiredbooleantrueSi un documento vencido se rechaza.
padronCrossCheckbooleanfalseSi se cruza contra el padrón oficial.
padronUnavailablePolicyenumreviewQué hacer si el padrón no responde: approve, review o reject.
nameMatchThresholdnumber0.85Similitud mínima de nombres, entre 0 y 1. Por debajo del piso duro rechaza; entre el piso y este valor, señal blanda.
maxCaptureAttemptsinteger3Intentos de captura antes de dar el paso por perdido. De 1 a 10.

face_liveness

CampoTipoPor defectoDescripción
challengeenumFaceMovementAndLightChallengeFaceMovementAndLightChallenge (el de mayor precisión, con secuencia de luces) o FaceMovementChallenge.
livenessThresholdnumber85Umbral de vivacidad, acotado a [70, 99]. Por debajo de 70 la prueba se vuelve decorativa; 100 la haría imposible.
compareFacesobjectausenteSi falta, el paso corre en modo sólo prueba de vida. Si está, define las bandas del face match.
compareFaces.autoApprovenumberSimilitud a partir de la cual se aprueba sin intervención. Entre 70 y 100.
compareFaces.reviewBand[number, number]Banda [mín, máx] que va a revisión manual. Cada extremo entre 70 y 100, con mín ≤ máx y autoApprove ≥ máx.
maxAttemptsinteger3Intentos de prueba de vida. De 1 a 5.

otp_email

CampoTipoPor defectoDescripción
ttlMinutesinteger10Vigencia del código. De 1 a 90 minutos.
maxAttemptsinteger5Intentos de ingreso. De 1 a 10.
maxResendsinteger3Reenvíos permitidos. De 0 a 10.
collectIfMissingbooleanfalseSi se le pide el email a la persona cuando no viene precargado.

otp_whatsapp

CampoTipoPor defectoDescripción
ttlMinutesinteger5Vigencia del código. De 1 a 90 minutos.
maxAttemptsinteger5Intentos de ingreso. De 1 a 10.
maxResendsinteger3Reenvíos permitidos. De 0 a 10.
fallbackToEmailbooleanfalseSi se cae a email cuando el envío por WhatsApp no prospera.
collectIfMissingbooleanfalseSi se le pide el teléfono a la persona cuando no viene precargado.

form

CampoTipoPor defectoDescripción
fieldsarrayAl menos un campo.
fields[].keystringClave alfanumérica sin espacios, arrancando con letra. Es la clave del resultado en result.form_data. Única dentro del paso.
fields[].labelstringEtiqueta visible. De 1 a 200 caracteres.
fields[].kindenumtext, select, date, number o checkbox.
fields[].requiredbooleanfalseSi el campo es obligatorio.
fields[].maxLengthintegersin límiteLargo máximo. De 1 a 10000.
fields[].optionsstring[]Obligatorio en los campos select; al menos una opción.

choice

CampoTipoPor defectoDescripción
optionsstring[]Ids de los pasos que son opciones de este grupo. Un paso que es opción no puede estar además en flow.sequence.
userSelectsbooleantrueSi elige la persona o el flujo. No cambia cuántas opciones corren.
minRequiredinteger1Cuántas opciones hay que completar. No puede superar la cantidad de opciones ofrecidas.

Reglas condicionales

Una regla dice: cuando el paso X termina con el resultado R, hacé A.

{
  "when": { "step": "documento", "result": "review" },
  "then": { "goto": "verificacion-extra", "resume": true }
}

when

CampoTipoRequeridoDescripción
stepstringId del paso que dispara la regla.
resultenumapproved, rejected, review o retries_exhausted (el paso agotó sus reintentos).

then

Exactamente una de estas cinco acciones:

CampoTipoAcciónDescripción
gotostringsaltarSalta al paso indicado. Con resume: true, al terminar ese paso el flujo retoma la continuación del paso que disparó la regla; sin resume, sigue desde donde esté el destino en la secuencia.
continuetrueseguirEl flujo sigue pese al resultado, pero el resultado sigue contando para la decisión final. Es "seguí, ya vamos a decidir al final".
skiptruetolerarDescarta el paso: su resultado no cuenta para la decisión final. Es "este validador es opcional".
rejecttrueterminarTermina la sesión como rechazada, en el acto.
reviewtrueterminarTermina la sesión en revisión manual, en el acto.

Pasos condicionales

Un paso puede declararse con entrada conditional: entonces sólo es alcanzable por una regla goto, y no puede estar en flow.sequence. Es la forma de definir ramas de excepción —una verificación extra que sólo corre cuando algo salió raro— sin que se ejecuten en el camino feliz.

Qué valida la plataforma al publicar

Además del schema, se valida la estructura del grafo. Una configuración que no pasa estas reglas no se puede publicar:

CampoTipoErrorDescripción
Referencias inexistentesEstructuraunknown_step_refLa secuencia, una regla o una opción de choice apunta a un id que no existe.
Ids duplicadosEstructuraduplicate_step_idDos pasos con el mismo id.
Opción en la secuenciaEstructurachoice_option_in_sequenceUn paso que es opción de un choice no puede estar además en flow.sequence.
Condicional en la secuenciaEstructuraconditional_in_sequenceUn paso con entrada conditional sólo se alcanza por goto.
Paso inalcanzableEstructuraunreachable_stepUn paso definido que no está en la secuencia, ni es opción de un choice, ni es destino de una regla.
CicloEstructuracycle_detectedEl grafo de avance tiene un ciclo. Incluye el caso de un goto hacia un paso anterior de la secuencia: el salto más el camino de secuencia cierra el bucle.
Sin camino al finalEstructurano_terminal_pathDesde algún paso alcanzable no hay forma de llegar al final del flujo.
Choice vacío o mal armadoEjecutabilidadchoice_empty, duplicate_choice_option, choice_min_requiredUn choice sin opciones, con una opción repetida, o que exige completar más opciones de las que ofrece.
Claves de formulario repetidasEjecutabilidadduplicate_form_field_keyDos campos con la misma key: las respuestas se pisarían entre sí.

Un paso alcanzado por un goto sin resume tiene una exigencia extra: como no hereda continuación, cada uno de los cuatro resultados posibles necesita su propia regla que encamine el flujo (goto sin resume, reject o review). Si sólo cubrís el caso de fallo, la rama "aprueba y sigue" queda sin salida y la configuración se rechaza. La forma más simple de evitarlo es usar resume: true.

Ejemplo completo

Un flujo con documento y biometría, donde un documento dudoso suma una verificación por WhatsApp antes de decidir:

{
  "schemaVersion": 2,
  "version": 3,
  "name": "Onboarding estándar",
  "steps": [
    {
      "id": "documento",
      "type": "document_ar",
      "params": {
        "acceptedDocs": ["dni_card", "passport"],
        "rejectExpired": true,
        "padronCrossCheck": true,
        "padronUnavailablePolicy": "review",
        "nameMatchThreshold": 0.85,
        "maxCaptureAttempts": 3
      }
    },
    {
      "id": "selfie",
      "type": "face_liveness",
      "params": {
        "challenge": "FaceMovementAndLightChallenge",
        "livenessThreshold": 85,
        "compareFaces": { "autoApprove": 92, "reviewBand": [80, 92] },
        "maxAttempts": 3
      }
    },
    {
      "id": "otp-refuerzo",
      "type": "otp_whatsapp",
      "params": {
        "entry": "conditional",
        "ttlMinutes": 5,
        "maxAttempts": 5,
        "collectIfMissing": true
      }
    }
  ],
  "flow": {
    "sequence": ["documento", "selfie"],
    "rules": [
      {
        "when": { "step": "documento", "result": "review" },
        "then": { "goto": "otp-refuerzo", "resume": true }
      },
      {
        "when": { "step": "selfie", "result": "retries_exhausted" },
        "then": { "review": true }
      }
    ]
  },
  "expiration": { "linkTtlHours": 48, "sessionIdleMinutes": 30 },
  "review": { "enabled": true, "slaHours": 24 },
  "webhookEvents": [
    "verification_session.approved",
    "verification_session.rejected"
  ],
  "webhookPayload": { "documentData": true }
}

Con la matriz de costos por defecto, ese flujo presupuesta 12 tokens: document_ar (5) + face_liveness (5) + el destino de la regla goto, otp_whatsapp (2). El paso condicional se cobra aunque no siempre corra: el presupuesto es de peor caso. Ver Prueba y producción.