Ciclo de vida de la transferencia saliente
Máquina de estados para transferencias salientes de Bre-B — transiciones, webhooks y estados terminales
Una transferencia saliente (un pago saliendo de tu cuenta por Bre-B) se mueve por una secuencia determinística de estados. Cada transición mapea a un evento de webhook, así que tu integración puede rastrear el progreso sin polling. Esta guía explica qué significa cada estado, qué hace que una transferencia se mueva entre ellos y cómo distinguir el éxito terminal de la falla terminal.
Diagrama de estados
Estados de un vistazo
| Estado | ¿Terminal? | Evento de webhook |
|---|---|---|
created | No | outgoing_transfer.created |
processing | No | outgoing_transfer.processing |
target_resolved | No | outgoing_transfer.target_resolved |
held | No | outgoing_transfer.held |
sent_to_breb_provider | No | outgoing_transfer.sent_to_breb_provider |
successful | Sí | outgoing_transfer.successful |
failed | Sí | outgoing_transfer.failed |
Recorrido detallado
1. created
Estado inicial. Una transferencia entra a este estado tan pronto como
create outgoing transfers
la acepta en un batch. La fila de la transferencia existe en nuestro sistema y tiene un id
que puedes usar para búsquedas.
Las transferencias rechazadas nunca llegan a este estado
Si el request falla la validación previa al insert — por ejemplo, el monto supera el límite
máximo, o el target es inválido — la transferencia se devuelve en el arreglo
rejected_transfers de la respuesta del batch y no se persiste. Nunca pasará por esta
máquina de estados y no se disparará ningún webhook. Ver Códigos de error de transferencia
saliente para la lista de motivos de
rechazo.
Qué pasa después: un worker en background toma la transferencia y la mueve
a processing. Esto típicamente ocurre en segundos.
2. processing
El sistema está procesando activamente la transferencia. El camino que toma depende de cómo se proveyó el target:
- Transferencias por plain-key (proveíste una llave Bre-B en
query): el sistema primero debe resolver la llave a un destinatario antes de poder retener fondos. - Target pre-resuelto (proveíste un
target_idexistente): la resolución se salta y la transferencia va directo al hold de fondos.
Siguientes estados:
- →
target_resolvedsi la resolución tiene éxito (camino plain-key) o el target ya estaba resuelto. - →
failedsi la resolución da error — por ejemplo,key_not_found,key_suspendedoinvalid_key_format.
3. target_resolved
La llave Bre-B del destinatario fue resuelta a un creditor concreto y una cuenta
bancaria. Si proveíste un expected_creditor, ahí es donde validamos que el
creditor resuelto coincida. Esta es la última oportunidad de rechazar la transferencia
antes de que los fondos se muevan.
Siguientes estados:
- →
helduna vez que los fondos están retenidos en la cuenta origen. - →
failedconstate_reason = target_creditor_mismatchsi el documento del creditor resuelto no coincide con elexpected_creditorque enviaste. Este es el resguardo que evita pagar a la persona equivocada cuando una llave se reasigna.
4. held
Fondos equivalentes al monto de la transferencia están reservados en tu cuenta origen. El dinero salió de tu saldo disponible pero todavía no fue enviado al banco del destinatario. En este punto la transferencia está totalmente comprometida desde tu lado.
Siguientes estados:
- →
sent_to_breb_provideruna vez que despachamos la instrucción de pago al proveedor Bre-B (Credibanco). - →
failedsi el hold mismo falla (raro en este punto — la mayoría de errores de fondos emergen antes).state_reasonllevará la causa (e.g.,insufficient_funds).
5. sent_to_breb_provider
El pago fue entregado al proveedor Bre-B para settlement. Ahora está en vuelo a través de la red Bre-B. La transferencia está esperando el callback final de settlement del proveedor.
Este estado puede mantenerse por un rato
La mayoría de transferencias settlea en segundos, pero timeouts del proveedor y la disponibilidad de los bancos aguas abajo pueden estirar esto a minutos. No asumas que una transferencia falló solo porque no salió rápido de este estado — espera el webhook terminal.
Siguientes estados:
- →
successfulcon un callback de settlement positivo. - →
failedcon un callback de settlement negativo (e.g.,breb_timeout,provider_unavailable,risk_controlo un fallbackunknown).
6. successful (terminal)
Los fondos fueron acreditados al destinatario. No hay más transiciones; no se disparan más webhooks para esta transferencia.
7. failed (terminal)
La transferencia no se completó. El campo state_reason en la transferencia (y
en el webhook outgoing_transfer.failed) te dice por qué. Dependiendo del
motivo, el error puede ser reintentable enviando una nueva transferencia — ver la
guía de reintentos en
Códigos de error de transferencia saliente.
Failed no es lo mismo que rejected
- Las transferencias rechazadas se devuelven de forma síncrona en
rejected_transferscuando creas un batch. Nunca se convierten en transferencias en nuestro sistema. - Las transferencias fallidas fueron aceptadas, persistidas, pasaron por al menoscreatedy luego golpearon un error. Tienen unstate_reasony emiten un webhook.
Trabajando con estados en tu integración
- Trata cada estado no terminal como transitorio. Una transferencia en
heldosent_to_breb_providersigue en vuelo — muéstrala como "en proceso" en tu UI en vez de "completa". - Trabaja con webhooks, no con polling. Cada transición de estado emite un webhook
con el nuevo estado y, en caso de falla, un
state_reason. Verifica cada webhook (ver Verificación de firma de webhook) y actualiza tu registro local de forma idempotente — el mismo estado puede ser reintentado por nuestros workers. - Maneja entrega fuera de orden. Las entregas de webhook son at-least-once. Si
ves
successfulantes quesent_to_breb_provider, conserva el estado más reciente e ignora el anterior. Una regla simple: si la transferencia ya está en un estado terminal localmente, ignora los webhooks no terminales. - Solo dos estados son finales.
successfulyfailedson los únicos estados terminales. Una vez que una transferencia alcanza cualquiera, nunca volverá a moverse.