Ciclo de vida de transferencia bancaria
Máquina de estados para transferencias bancarias y lotes — transiciones, webhooks y estados terminales.
Una transferencia bancaria es una dispersión: el dinero sale de tu cuenta Mono y llega a la cuenta bancaria colombiana de un beneficiario por uno de tres rails — ACH, Transfiya o Mono Turbo. Cada transferencia que creas vía POST /transfers se agrupa automáticamente en un lote, y tanto la transferencia como el lote se mueven por sus propias máquinas de estados en paralelo. Esta página explica qué significa cada estado, qué causa cada transición y qué evento de webhook se dispara en cada paso.
Entender ambas máquinas de estados importa porque tu handler de webhooks recibirá eventos del lote (¿fue autorizado? ¿enviado?) y eventos de la transferencia (¿fue aprobada o rechazada la transferencia individual?) en un flujo intercalado.
Máquina de estados de la transferencia
Estados de la transferencia de un vistazo
| Estado | ¿Terminal? | Evento de webhook |
|---|---|---|
created | No | — (respuesta síncrona de la API) |
in_progress | No | bank_transfer_fallback_routing se dispara si ocurre un reintento de routing |
approved | Sí | bank_transfer_approved |
declined | Sí | bank_transfer_rejected |
cancelled | Sí | — (iniciado por el usuario antes del despacho; sin evento asíncrono) |
duplicated | Sí | — (se expone vía batch_duplicated a nivel del lote) |
Los estados finales pueden cambiar
El evento bank_transfer_change_final_state se dispara en el caso raro en que una
transferencia ya en approved o declined es movida a un estado final distinto por el rail.
Maneja siempre este evento de forma idempotente — actualiza tu registro local al nuevo estado
incluso si ya lo considerabas settled.
Máquina de estados del lote
Cada llamada a POST /transfers crea un lote, incluso cuando envías una sola transferencia. El lote es la unidad que pasa por la autorización del admin (OTP) si tu cuenta lo requiere.
Estados del lote de un vistazo
| Estado | ¿Terminal? | Evento de webhook |
|---|---|---|
created | No | — (respuesta síncrona de la API) |
pending_otp | No | batch_authorization_requested |
verified_otp | No | — (interno; transiciona inmediatamente a processing_transactions) |
processing_transactions | No | batch_sent |
approved | Sí | — (las transferencias individuales emiten bank_transfer_approved) |
partially_approved | Sí | — (mezcla de bank_transfer_approved y bank_transfer_rejected por transferencia) |
declined | Sí | — (las transferencias individuales emiten bank_transfer_rejected) |
duplicated | Sí | batch_duplicated |
canceled | Sí | batch_canceled |
Recorrido detallado
Transferencia: created
Estado inicial. La fila de la transferencia existe en el sistema con un id que puedes usar para consultas. Mono aceptó la solicitud pero aún no la ha enviado al rail. Cada transferencia del lote entra a este estado al mismo tiempo.
Qué pasa después: una vez que el lote pasa cualquier gate de OTP y llega a processing_transactions, cada transferencia pasa a in_progress.
Transferencia: in_progress
La transferencia fue despachada al rail seleccionado — ACH, Mono Turbo o Transfiya. Los fondos están en tránsito; el saldo de la cuenta origen fue debitado pero el destino aún no ha confirmado el crédito.
Si el rail primario rechaza el intento y configuraste un fallback_routing, Mono reintenta automáticamente. Esto dispara bank_transfer_fallback_routing sin cambiar el estado de la transferencia — la transferencia se queda en in_progress durante el reintento.
Estados siguientes:
- →
approvedcuando el rail confirma el crédito. - →
declinedcuando el rail rechaza y ningún fallback tiene éxito.
Transferencia: approved (terminal)
El banco destino confirmó el crédito. Los fondos están en la cuenta del beneficiario. El webhook bank_transfer_approved se dispara con el routing final y el monto. No ocurren más transiciones bajo circunstancias normales.
Transferencia: declined (terminal)
La transferencia fue rechazada. El campo declination_reason en el payload del webhook lleva la razón del rechazo (por ejemplo, insufficient_funds o invalid_account_information). El webhook bank_transfer_rejected se dispara. No ocurren más transiciones bajo circunstancias normales.
Transferencia: cancelled (terminal)
La transferencia fue cancelada por un usuario antes de que el lote llegara a processing_transactions. Cancelar un lote cancela todas sus transferencias. No se dispara un webhook asíncrono para las transferencias individuales — el lote emite batch_canceled.
Transferencia: duplicated (terminal)
El entity_id de esta transferencia ya se usó en un lote anterior. La transferencia no se procesa. El lote emite batch_duplicated.
Lote: created
La fila del lote existe en el sistema. Todas sus transferencias están en created. Este estado es síncrono — obtienes el id del lote en la respuesta de la API.
Qué pasa después: Mono chequea si el lote requiere autorización OTP.
Lote: pending_otp
El lote requiere autorización explícita de un usuario Administrador antes de poder ser procesado. El webhook batch_authorization_requested se dispara. Ninguna transferencia se mueve hasta que un admin autorice.
Estados siguientes:
- →
verified_otpcuando un admin completa la autorización. - →
canceledcuando un admin cancela.
Lote: verified_otp
El admin autorizó. Mono prepara el lote para el despacho. Este estado es transitorio — pasa a processing_transactions en segundos y no se dispara webhook para verified_otp.
Lote: processing_transactions
El lote está siendo despachado. Cada transferencia pasa a in_progress. El webhook batch_sent se dispara. Desde este punto, los webhooks individuales de transferencia (bank_transfer_approved / bank_transfer_rejected) guían la conciliación.
Lote: approved (terminal)
Todas las transferencias del lote fueron aprobadas. Los eventos individuales bank_transfer_approved ya se dispararon para cada transferencia.
Lote: partially_approved (terminal)
Algunas transferencias fueron aprobadas y otras rechazadas. Cada transferencia ya emitió su propio evento bank_transfer_approved o bank_transfer_rejected. Concilia transferencia por transferencia usando los eventos individuales.
Lote: declined (terminal)
Todas las transferencias del lote fueron rechazadas. Los eventos individuales bank_transfer_rejected ya se dispararon.
Lote: duplicated (terminal)
Cada transferencia del lote tenía un entity_id que ya existía. Ninguna transferencia se procesa. batch_duplicated se dispara. Las transferencias individuales pasan a duplicated.
Lote: canceled (terminal)
Un admin canceló el lote antes de que llegara a processing_transactions. Todas las transferencias pasan a cancelled. batch_canceled se dispara.
Cómo trabajar con estas máquinas de estados en tu integración
-
Apóyate en webhooks, no en polling. Cada transición significativa emite un webhook. Suscríbete a eventos de transferencia y de lote — llevan información distinta y llegan en orden intercalado.
-
Maneja entregas fuera de orden. Las entregas de webhooks son at-least-once y pueden llegar fuera de orden. Si recibes
bank_transfer_approvedantes quebatch_sent, conserva el estado más reciente e ignora el anterior. Regla segura: si una transferencia ya está en estado terminal localmente, ignora los webhooks no terminales para ella. -
Diseña para el gate de OTP. Si tu cuenta Mono requiere autorización de lote, tu lote se quedará en
pending_otphasta que un admin actúe. Planea tu UX alrededor de esto: expón el estado del lote a los operadores para que sepan cuándo se requiere acción. -
Distingue el estado de la transferencia del estado del lote. Un lote en
partially_approvedno te dice qué transferencias tuvieron éxito — debes inspeccionar los eventos individuales de transferencia. Concilia transferencia por transferencia, no lote por lote. -
Maneja
bank_transfer_change_final_state. Es raro, pero una transferencia ya enapprovedodeclinedpuede pasar a un estado final distinto. Actualiza tu registro local cuando llegue este evento; no trates un estado terminal como inmutable hasta que dejes de recibir eventos para esa transferencia. -
Usa
entity_idpara idempotencia. Enviar el mismoentity_iddos veces resulta enduplicated. Esta es tu red de seguridad contra el doble envío — pero también significa que debes generar unentity_idnuevo para cada transferencia genuinamente nueva.
Siguientes pasos
- Webhooks: transferencias bancarias — los esquemas de payload completos de cada evento anterior.
- Flujo de envío de transferencias — la secuencia de punta a punta que mueve una transferencia por estos estados.
- Sandbox: transferencias bancarias — simula el ciclo de vida completo sin mover dinero real.