Migrar de Dispersión a Terceros a Dispersiones a Terceros vía SPEI
Guía de migración para negocios que ya usan /payees + /transfers y quieren pasar al nuevo flujo de payout_rules
Si tu integración dispersa dinero a terceros con POST /payees y POST /transfers, esta guía te muestra qué reemplaza a qué en Dispersiones a Terceros vía SPEI, y los cambios de comportamiento que rompen una migración hecha 1:1.
El flujo nuevo separa dos cosas que antes iban juntas: el beneficiario, que ahora se valida contra el banco antes de poder recibir dinero, y la instrucción de dispersión, que ahora es una regla que Conekta ejecuta por ti — una vez, en una fecha, o con una frecuencia — en lugar de una orden que disparas cada vez desde tu código.
¿Sigue funcionando mi integración vieja?Sí — ambos flujos operan en paralelo hoy. Puedes migrar por partes: mover un beneficiario al flujo nuevo no afecta a los que sigues dispersando con
/transfers. Confirma con tu Ejecutivo de Cuentas o [email protected] si tu negocio tiene una fecha de corte planeada para/payeesy/transfers.
Qué ganas al migrar
- Validación de la cuenta destino antes de mandar dinero. Conekta confirma con el banco que la CLABE existe y corresponde al titular declarado. Una CLABE mal capturada se detecta en el alta, no cuando el dinero ya salió.
- Programación y recurrencia sin código propio. Los pagos a fecha fija o con periodicidad se configuran como reglas (
rule_type: scheduledorecurrent). Si hoy mantienes un cron que llama a/transfers, puedes retirarlo. - Alta masiva de beneficiarios por CSV.
- Consulta de estado por API —
GET /payouts/disbursementsyGET /payouts/batches, sin depender de haber guardado el payload del webhook. - Cancelación antes de la ejecución — una instrucción
one_timeque todavía no se ejecutó se puede eliminar; en/transfersno había forma de dar marcha atrás. - Fondeo propio por SPEI, además del saldo generado por tus cobros en Conekta.
Lo que no cambia es el tiempo: igual que con /transfers, una instrucción se crea para un día posterior y se ejecuta ese día en el proceso nocturno.
Qué reemplaza a qué
| Acción | Antes (v2.2.0) | Ahora (SPEI) | Nota |
|---|---|---|---|
| Crear beneficiario | POST /payees — objeto payee con destinations[] | POST /payouts/payees (doc) — objeto external_payee, una sola clabe | Un beneficiario por CLABE — ver Un payee ahora es una sola CLABE |
| Editar beneficiario | Editabas destinations[] | PATCH /payouts/payees/{id} — solo alias, rfc, email, o pausar/reactivar | No puedes editar name ni clabe — ver Editar un beneficiario tiene límites |
| Alta masiva | No existía | POST /payouts/payees/bulk (CSV) | Funcionalidad nueva |
| Enviar dinero puntual | POST /transfers (ejecución inmediata al siguiente día hábil) | POST /payout_rules con rule_type: one_time (doc) | Crea una instrucción, no un pago — ver La instrucción no es una orden de ejecución |
| Pago programado o recurrente | Lo resolvías con un cron propio | POST /payout_rules con rule_type: scheduled o recurrent | Puedes retirar tu scheduler |
| Cancelar una instrucción | Nunca — no se podía cancelar una vez enviada | DELETE /payout_rules/{id} | Sin restricción para scheduled/recurrent; one_time solo mientras no se haya ejecutado |
| Reintentar un pago fallido por saldo | No existía | POST /v1/payouts/instructions/{id}/retry (manual), o auto_retry_on_insufficient_funds en la instrucción (automático, opt-in) | Ver Seguimiento de dispersiones |
| Consultar el estado de un pago | Vía el payload del webhook | GET /payouts/disbursements, GET /payouts/payees/{id}/disbursements (doc) | — |
| Consultar el proceso del día | No existía | GET /payouts/batches | — |
| Webhooks de dispersión | payout.created, payout.paid_out, payout.failed | payout.created, payout.in_transit, payout.paid_out, payout.failed, payout.retrying | in_transit y retrying son nuevos |
| Webhooks de beneficiario | No existían | payee.payout_method.created / .updated / .deleted | — |
| Fondeo | Solo desde saldo generado por cobros en Conekta | Saldo disponible o depósito directo por SPEI | — |
Cómo funciona el flujo nuevo
sequenceDiagram
autonumber
participant Tu as Tu servidor
participant Conekta
participant Banco as Banco del beneficiario
Tu->>Conekta: POST /payouts/payees (alias + name + clabe)
Conekta-->>Tu: external_payee · pending_validation
Conekta->>Banco: Validación de la cuenta destino
Banco-->>Conekta: Resultado
Conekta-->>Tu: Webhook payee.payout_method.updated · validated_active
Tu->>Conekta: POST /payout_rules (payee_id, amount en pesos, rule_type)
Conekta-->>Tu: Instrucción creada · scheduled
Note over Conekta: Llega el día de la instrucción y hay fondos en la cuenta de dispersiones
Conekta->>Banco: Transferencia SPEI
Conekta-->>Tu: Webhook payout.in_transit → payout.paid_out
- Das de alta al beneficiario con
POST /payouts/payees. La respuesta traevalidation_status: pending_validation: el beneficiario existe pero todavía no puede recibir dinero. - Conekta valida la cuenta contra el banco (normalmente menos de dos minutos). El resultado te llega por el webhook
payee.payout_method.updated, o lo consultas conGET /payouts/payees/{id}. - Creas la instrucción con
POST /payout_rules, apuntando alpayee_idya validado. - Conekta ejecuta la instrucción el día que corresponde, cuando hay fondos disponibles en tu cuenta de dispersiones.
- Sigues el resultado por los webhooks
payout.*o consultandoGET /payouts/disbursements.
Estados del beneficiario
Solo un beneficiario en validated_active puede recibir dispersiones:
stateDiagram-v2
[*] --> pending_validation: POST /payouts/payees
pending_validation --> validated_active: cuenta confirmada
pending_validation --> rejected: la validación falla
validated_active --> validated_inactive: PATCH validation_status
validated_inactive --> validated_active: PATCH validation_status
rejected --> [*]: eliminar y recrear
Importante
PATCHnunca dispara una nueva validación: solo editaalias/rfc/validation_statusentrevalidated_activeyvalidated_inactive. No puedes editarnameniclabe. Si capturaste mal el nombre del titular o la CLABE, la corrección es eliminar el beneficiario — sualiasqueda libre — y crear uno nuevo. Un beneficiariorejectedtampoco se puede reactivar; solo eliminarse y recrearse.
Cambios que no son 1:1
Estos cinco puntos son los que causan incidentes en una migración hecha por reemplazo directo de endpoints.
Un payee ahora es una sola CLABE
Un payee podía tener varios destinations[] bajo un mismo payee_id, con un default_destination_id. El external_payee tiene un solo campo clabe.
Si tenías un beneficiario con dos o más destinos, en el flujo nuevo son dos o más beneficiarios distintos, cada uno con su propio id. Usa el campo alias (obligatorio) para distinguirlos — es el rol que antes cumplía default_destination_id.
Los payee_id del flujo viejo no sirven en el flujo nuevo. Si tu base de datos guarda payee_id de /payees, necesitas una tabla de mapeo: cada beneficiario recreado devuelve un id nuevo.
Editar un beneficiario tiene límites
PATCH /payouts/payees/{id} solo acepta alias, rfc, email, y el cambio de validation_status entre validated_active y validated_inactive. No existe una forma de editar name ni clabe — si tu integración vieja corregía datos de un destination en el mismo payee, ese patrón no existe aquí: tienes que eliminar el beneficiario mal capturado y crear uno nuevo.
La validación del beneficiario puede fallar
POST /payees devolvía un beneficiario listo para usar. POST /payouts/payees inicia una validación bancaria asíncrona, y rejected es un desenlace normal, no un error de tu integración — un beneficiario rechazado no puede recibir dispersiones y no se arregla reintentando la misma carga: hay que corregirlo (eliminar y recrear) o contactar soporte si la CLABE es correcta. Contempla ese estado en tu manejador de payee.payout_method.updated desde el primer día.
ImportanteSi tu integración vieja creaba el payee y creaba la dispersión inmediatamente después, ese patrón ya no funciona.
POST /payout_rulesresponde422si elpayee_idno está envalidated_active. Espera el webhookpayee.payout_method.updated, o haz polling aGET /payouts/payees/{id}, antes de crear la primera instrucción de un beneficiario nuevo.
La instrucción no es una orden de ejecución
POST /transfers era una orden: se creaba y se pagaba. POST /payout_rules crea una instrucción que Conekta evalúa y ejecuta el día que corresponde según el rule_type — one_time en el próximo proceso nocturno, scheduled en la fecha que definas, recurrent según la frecuencia. Si tu código asumía que crear la transferencia equivalía a haberla pagado, esa suposición hay que reemplazarla por seguir los webhooks payout.* o consultar GET /payouts/disbursements.
El monto cambia de unidad
POST /transfers recibía amount en centavos como entero. POST /payout_rules recibe amount en pesos como string.
| Antes | Ahora | |
|---|---|---|
| $50.00 MXN | "amount": 5000 | "amount": "50.00" |
| $1,250.75 MXN | "amount": 125075 | "amount": "1250.75" |
ImportanteEste es el cambio que rompe una migración sin dar error. Si reenvías el valor en centavos como string,
"5000"es un string válido que la API acepta — y acabas de crear una instrucción de $5,000.00 en lugar de $50.00. Verifica el monto de tus primeras dispersiones en modo de pruebas antes de mover tráfico real.
Los montos que consultas (GET /payouts/disbursements, GET /payouts/batches) siguen en centavos. La unidad en pesos aplica solo al crear o editar la instrucción.
Próximamente
Vas a poder configurar un límite máximo de monto por beneficiario: toda instrucción que lo exceda se rechazará antes de ejecutarse, y un beneficiario sin límite configurado eventualmente no podrá recibir pagos. Todavía no está disponible en v2.3.0 — no lo asumas en tu integración; te avisaremos cuando salga.
Cómo migrar tu integración
- Actualiza la versión de API. Cambia el header
Accept— o su equivalente en tu SDK — aapplication/vnd.conekta-v2.3.0+json. - Recrea tus beneficiarios con
POST /payouts/payees, uno por CLABE. Si son muchos, usa el alta masiva por CSV. - Guarda el mapeo
payee_idviejo →idnuevo en tu base de datos. - Espera
validated_activeantes de crear cualquier instrucción para ese beneficiario, y maneja el desenlacerejected. - Reemplaza cada
POST /transfersporPOST /payout_rules: las llamadas ad hoc pasan arule_type: one_time; lo que tu cron disparaba en fechas fijas o con periodicidad pasa ascheduledorecurrent, y retiras el cron. - Convierte
amountde centavos a pesos-string en el punto donde armas el body de la instrucción. - Actualiza tu manejador de webhooks: agrega
payout.in_transit,payout.retrying, y los nuevospayee.payout_method.*. La verificación de firma es obligatoria. - Decide tu estrategia de reintento por saldo insuficiente: activa
auto_retry_on_insufficient_fundsen las instrucciones que quieras que se recreen solas (hasta 5 días hábiles), o maneja el reintento manual desde tu código conPOST /v1/payouts/instructions/{id}/retry. - Reemplaza tu lógica de conciliación por
GET /payouts/disbursementsoGET /payouts/batchesen lugar de depender del payload del webhook que guardaste. - Prueba el flujo completo en modo de pruebas — montos, beneficiarios validados y webhooks recibidos — antes de cambiar a llaves productivas.
- Coordina la fecha de corte del flujo viejo con tu Ejecutivo de Cuentas o [email protected].
Antes y después, en código
Crear el beneficiario:
# Antes — v2.2.0
curl -X POST https://api.conekta.io/payees \
-H "Accept: application/vnd.conekta-v2.2.0+json" \
-H "Authorization: Bearer key_xxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Juan Pérez García",
"destinations": [
{ "type": "clabe", "account_number": "646180157034084695" }
]
}'# Ahora — v2.3.0
curl -X POST https://api.conekta.io/payouts/payees \
-H "Accept: application/vnd.conekta-v2.3.0+json" \
-H "Authorization: Bearer key_xxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"alias": "proveedor-norte",
"name": "Juan Pérez García",
"clabe": "646180157034084695"
}'La respuesta trae el beneficiario en validación:
{
"id": "payee_xxxxxx",
"object": "external_payee",
"name": "Juan Pérez García",
"alias": "proveedor-norte",
"clabe": "**************4695",
"bank_code": "646",
"validation_status": "pending_validation"
}Enviar $50.00 MXN a ese beneficiario:
# Antes — v2.2.0 · amount en centavos, ejecución implícita
curl -X POST https://api.conekta.io/transfers \
-H "Accept: application/vnd.conekta-v2.2.0+json" \
-H "Authorization: Bearer key_xxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "payee_id": "payee_xxxxxx", "amount": 5000 }'# Ahora — v2.3.0 · amount en pesos como string, crea una instrucción
curl -X POST https://api.conekta.io/payout_rules \
-H "Accept: application/vnd.conekta-v2.3.0+json" \
-H "Authorization: Bearer key_xxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"rule_type": "one_time",
"payee_id": "payee_xxxxxx",
"amount": "50.00",
"name": "Liquidación proveedor"
}'Errores: qué cambia
El catálogo genérico de /transfers (authentication_error, processing_error por saldo insuficiente, parameter_validation_error por CLABE o payee inválidos) se reemplaza por respuestas 422 específicas por endpoint, documentadas en cada página:
- Errores de
POST /payouts/payees→ Gestión de beneficiarios - Errores de
POST /payout_rules→ Tipos de dispersión
| Situación | Antes | Ahora |
|---|---|---|
| Beneficiario aún no validado | No existía el estado | 422 al crear la instrucción; espera validated_active |
| CLABE que el banco no reconoce | Fallaba al transferir | El beneficiario queda en rejected en el alta, antes de mover dinero |
| Saldo insuficiente el día de la ejecución | processing_error · Insufficient funds, sin reintento | La dispersión queda failed. Se recrea sola al siguiente día hábil solo si la instrucción tiene auto_retry_on_insufficient_funds activo (opt-in, hasta 5 intentos, evento payout.retrying); si no, reintenta manualmente con POST /v1/payouts/instructions/{id}/retry |
Un reintento manual no consume el tope de 5 días del reintento automático — puedes seguir reintentando a mano aunque la cadena automática ya se haya agotado. Ver Reintentar una dispersión fallida por saldo insuficiente.
Related
- Dispersión a Terceros — resumen (v2.2.0) — el flujo que estás migrando.
- Dispersiones a Terceros vía SPEI — resumen — visión general del flujo nuevo.
- Gestión de beneficiarios — alta, validación, edición y CSV masivo.
- Tipos de dispersión —
payout_rules, tipos y prioridad de ejecución. - Seguimiento de dispersiones — consulta de batches, disbursements y reintentos.
- Verificación de firma de webhooks — obligatoria para procesar eventos
payout.*.
Updated 5 days ago

