Composición de flows + matriz LoA
Una de las confusiones más comunes cuando alguien ve por primera vez el seed default de bmonkey es pensar que el flujo “consentimiento + documento
- selfie + SARLAFT + OTP” es el onboarding que ofrece bjungle. No lo es. Es un ejemplo (el caso cashpaya). bmonkey está diseñado como un workflow engine de identidad: cada tenant define qué pasos quiere, en qué orden, y el nivel de aseguramiento (LoA) resultante se calcula a partir de la combinación.
Este ADR fija:
- El catálogo canónico de pasos.
- La matriz de cuánto LoA aporta cada combinación.
- Las verticales pre-seedeadas para acelerar setup.
- El modelo de pricing por step.
El catálogo de pasos
Sección titulada «El catálogo de pasos»StepCatalog (en internal/domain/flow.go) es la fuente única de
verdad. La portal lo lee vía GET /v1/flows/catalog y el step picker
se renderiza dinámicamente. Agregar un step nuevo es agregar una entrada
a este array + el handler en flow_engine.go. Cero migración.
| Step | Qué hace | Açaí cost | Valid in |
|---|---|---|---|
consent | Registra HABEAS DATA + biométrico en subject_consents | 0 | onboarding |
document_upload | Captura frente / dorso del documento (presign S3) | 0 | onboarding |
ocr | Extrae datos del documento con Bedrock | 0 | onboarding |
face_match | Compara selfie vs foto del documento (Rekognition / ArcFace) | 0 | onboarding, login, step-up |
face_enroll | Guarda patrón facial para login posterior | 0 | onboarding |
liveness | Anti-spoofing (Rekognition Liveness o básico) | 0 | onboarding |
sarlaft | Screening sincrónico contra reglas bhawk (PEP, OFAC, etc.) | screening | onboarding |
email_otp | Código por correo (SES) | 0 | onboarding, login, step-up |
sms_otp | Código por SMS (SNS) | mfa_otp_sms | onboarding, login, step-up |
password | Contraseña tradicional (bmonkey como IdP) | 0 | onboarding, login |
La matriz LoA → pasos mínimos
Sección titulada «La matriz LoA → pasos mínimos»LoA = Level of Assurance, alineado con NIST 800-63-3 y eIDAS. bmonkey emite VCs con uno de tres niveles según qué pasos se completaron.
┌─────────┬─────────┬─────────┐ │ LoA 1 │ LoA 2 │ LoA 3 │ │ (bajo) │ (medio) │ (alto) │─────────────────┼─────────┼─────────┼─────────┤consent │ ✓ │ ✓ │ ✓ │email_otp │ ✓ │ ✓ │ ✓ │document_upload │ │ ✓ │ ✓ │ocr │ │ ✓ │ ✓ │face_enroll │ │ ✓ │ ✓ │face_match │ │ ✓ │ ✓ │liveness │ │ │ ✓ │sarlaft │ │ │ ✓ │sms_otp │ │ │ ✓ │─────────────────┴─────────┴─────────┴─────────┘EquivalenciasNIST 800-63-3 IAL1 IAL2 IAL3eIDAS low substantial highReglas duras:
- Sin
consent→ no se emite VC (es bloqueo legal, no técnico). - Sin
document_upload + ocr + face_match→ LoA queda en 1. - Sin
liveness→ LoA capped a 2. - Sin
sarlaft→ no se otorga LoA 3 a menos que el tenant haga su propio screening por fuera (raro, requiere acuerdo explícito).
Cuando un tenant pide presentación con required_loa: 2, el wallet
solo aprueba si hay al menos una VC del usuario que llegó a ese nivel.
Eso permite que la misma persona se onboardee LoA1 en una app de
delivery y LoA3 en un banco, y cada tenant solo ve / paga lo que le
corresponde.
Verticales pre-seedeadas
Sección titulada «Verticales pre-seedeadas»Para acelerar el primer flow de un tenant, ofrecemos seeds por vertical. Cada seed inserta un flow publicado con steps + branding razonables.
delivery_lite (LoA 1)
Sección titulada «delivery_lite (LoA 1)»[consent, email_otp]
UX: ~30 segundosAçaí cost por onboarding: ~5Casos típicos: apps de pedidos, fitness, contenido, suscripciones bajo riesgofintech_medium (LoA 2)
Sección titulada «fintech_medium (LoA 2)»[consent, document_upload, ocr, face_enroll, face_match, email_otp]
UX: ~3 minutosAçaí cost por onboarding: ~30Casos típicos: billeteras digitales sin custodia mayor, marketplaces con pago integrado, neobancos litebanking_full (LoA 3)
Sección titulada «banking_full (LoA 3)»[consent, document_upload, ocr, face_enroll, face_match, liveness, sarlaft, email_otp, sms_otp]
UX: ~6 minutosAçaí cost por onboarding: ~120 (incluye screening + SMS)Casos típicos: apertura de cuenta corriente, líneas de crédito, corredoras bursátilescrypto_strict (LoA 3 + re-attestation periódica)
Sección titulada «crypto_strict (LoA 3 + re-attestation periódica)»Como banking_full, más:
- Trust policy con
require_freshness_days: 90— fuerza re-presentación cada trimestre. - Step-up obligatorio en transferencias > X via flow
step_up.
flow tipo onboarding:[consent, document_upload, ocr, face_enroll, face_match, liveness, sarlaft, email_otp, sms_otp]
flow tipo step_up:[face_match, sms_otp]Cómo configurar un flow custom
Sección titulada «Cómo configurar un flow custom»Desde el portal
Sección titulada «Desde el portal»- Tenant-portal :4401 → tab bmonkey → Flows → Nuevo flow.
- Tipo (onboarding / login / step_up) + code (slug interno).
- Step picker: arrastra steps del catálogo.
- Por step: marcar
required(si es opcional, se omite si el contexto no lo necesita) + editarconfig. - Branding (logo, color accent, texto del header).
- Save as draft → testear con
/embed/<token>→ Publish.
Desde API
Sección titulada «Desde API»curl -X POST http://localhost:8081/v1/flows \ -H "X-API-Key: dj_live_anafintech_XXX" \ -H "Content-Type: application/json" \ -d '{ "type": "onboarding", "code": "delivery_lite", "name": "Onboarding rapido para delivery", "version": 1, "steps": [ {"step_type": "consent", "required": true, "config": {}}, {"step_type": "email_otp", "required": true, "config": {"ttl_seconds": 300}} ], "branding": {"accent": "#ff5722", "logo_url": "https://ana.co/logo.png"}, "trust_policy": {"required_loa": "LoA1"} }'
# luego publicar:curl -X POST http://localhost:8081/v1/flows/{flow_id}/publish \ -H "X-API-Key: dj_live_anafintech_XXX"Branching condicional (avanzado)
Sección titulada «Branching condicional (avanzado)»Los pasos pueden tener config.condition que los SALTA si el contexto
no lo satisface. Ejemplo: SMS OTP solo si el usuario proveyó teléfono.
{ "step_type": "sms_otp", "required": false, "config": { "condition": { "key": "phone", "op": "exists" } }}Operadores soportados hoy: exists, absent, equals, not_equals.
Esto permite:
- MFA opcional: SMS OTP solo si registraron celular.
- Step-up por riesgo:
face_matchsolo si el monto > 1M. - Geofencing:
sarlaftextra solo si el doc es de país de alto riesgo.
Pricing implícito
Sección titulada «Pricing implícito»El costo Açaí por onboarding completo se calcula sumando los
acai_operation de cada step que ejecutó. Eso significa:
delivery_litecuesta lo mínimo (algunos Açaí por la sesión + 0 por steps gratis).banking_fullcuesta significativamente más (screening+mfa_otp_sms- costos base).
- Ana paga exactamente lo que usó por usuario onboardeado, no una cuota fija por nivel.
Esto está alineado con el modelo de cashpaya: cobramos por uso real, no por capacidad reservada.
Implicaciones para otros ADRs
Sección titulada «Implicaciones para otros ADRs»- ADR-001 separación de audiencias: cada tenant decide qué pasos son aceptables para SU end user. bjungle no impone política mínima excepto en regulación nacional dura (SARLAFT obligatorio si el tenant ofrece servicios financieros en Colombia bajo Decreto 1421 / SuperFinanciera).
- Wallet Modelo C: la VC emitida lleva
loa: "LoA1|2|3"y elproofcon eldid:webdel issuer. La trust policy del tenant que pide la presentación es quien decide si ese LoA basta. - Trust policy: el
required_loaen la trust policy del tenant solicitante se compara contra el LoA del VC en el wallet. Mismatch → consent rechaza conloa_insufficient.
Anti-patrones
Sección titulada «Anti-patrones»- Tenant que omite
consentporque “queremos UX más rápido”. No, el step es no-negociable (HABEAS DATA). El flowdelivery_liteya lo tiene como mínimo. - Tenant que pone
sarlaften una app de fitness porque “más seguridad es mejor”. No: SARLAFT cuesta 40 Açaí, agrega 1-2 minutos de UX, y no tiene sentido fuera de servicios financieros. La vertical correcta esdelivery_lite. - Tenant que pide
required_loa: 3para ver elfull_name. Crea fricción innecesaria.required_loa: 2(documento + cara) es suficiente para “verificar nombre real”. LoA3 es para movimientos financieros.
Decisión
Sección titulada «Decisión»| Aspecto | Decisión |
|---|---|
| Status | Accepted (2026-05-30) |
| Aplicable a | bmonkey-api · flow_engine · portal · embed |
| Catálogo de steps | open vocabulary — agregar entrada al StepCatalog + handler |
| Verticales seedeadas | delivery_lite, fintech_medium, banking_full, crypto_strict |
| Re-evaluar | cuando un regulador imponga LoA mínimo por vertical |