Skip to navigation

Mover dinero

Todo el dinero que sale de tu balance se mueve con un solo endpoint: POST /api/transfers. Indicas a dónde va el dinero con el objeto destination, y Recurrente crea el movimiento correcto — un retiro bancario, una transferencia a otra cuenta de Recurrente, un envío a un teléfono, o un envío de stablecoin on-chain.

Este endpoint está disponible únicamente en LIVE. El balance de un Sandbox es falso, no transferible y no retirable, por lo que POST /api/transfers rechaza cualquier destino sin debitar el ledger.

POST /api/transfers
{
"amount_in_cents": 50000,
"currency": "GTQ",
"destination": "ba_abc123"
}

Destinos

En la mayoría de los casos, destination es simplemente el identificador del destino como string — pega el que tengas y el formato indica a dónde va el dinero:

destinationA dónde va el dineroRegistro creado
"ba_abc123"Retiro a esa cuenta bancaria tuyawi_
"ac_xyz789"Transferencia a esa cuenta de Recurrentetr_
"@mitienda"Transferencia a la cuenta con ese handletr_
"co_maria123"Transferencia al teléfono de ese contacto guardadotr_
"+50255667788"Envío a ese teléfonotr_
{ "amount_in_cents": 50000, "destination": "ba_abc123" }
{ "amount_in_cents": 10000, "currency": "GTQ", "destination": "@mitienda", "note": "Pago de servicios" }
{ "amount_in_cents": 5000, "currency": "GTQ", "destination": "+50255667788" }

El único destino que necesita la forma de objeto es stablecoin, porque una dirección cruda no dice qué token ni en qué red enviarlo:

{
"amount_in_cents": 10000,
"currency": "USD",
"destination": { "type": "crypto_address", "address": "0x1234…", "chain": "base", "currency": "USDC" }
}

La forma de objeto también existe para los demás tipos si prefieres ser explícito: { "type": "bank_account", "id": "ba_..." }, { "type": "account", "id": "ac_... | @handle" }, { "type": "phone_number", "number": "..." }.

Notas por destino:

  • Retiros (ba_) — acepta además is_instant (retiro instantáneo, sujeto a elegibilidad y comisión) y should_perform_currency_conversion (convierte el balance a la moneda de la cuenta destino). Si omites currency, se usa la moneda de la cuenta bancaria.
  • Cuentas (ac_ / @handle) — la transferencia no tiene comisión y requiere evaluación antes de acreditarse.
  • Teléfonos y contactos — después de aprobarse, el movimiento queda unclaimed hasta que el destinatario se registre y complete KYC. Puedes cancelarlo mientras no haya sido reclamado.
  • Stablecoin — requiere completar la verificación de stablecoin en la app. Indica la stablecoin en destination.currency y la red en destination.chain.

Estados

Cada movimiento tiene un status canónico — el mismo vocabulario sin importar el destino — y un status_detail con el estado crudo del registro subyacente.

statusSignificado
pendingCreado, esperando procesamiento o revisión
in_reviewEn revisión manual o de riesgo
processingAprobado, en preparación para enviarse
sentFondos enviados por su rail. Para retiros bancarios locales, es un resultado terminal exitoso: ya no esperes completed.
unclaimedEnvío a teléfono esperando que el destinatario lo reclame
completedEl dinero llegó a su destino
failedRechazado o falló; el balance se restaura
cancelledCancelado antes de enviarse; el balance se restaura

Los P2P, retiros y envíos de stablecoin son asíncronos: la respuesta del POST trae el estado inicial, y puedes seguir el avance consultando GET /api/transfers/{id} o con webhooks.

Los P2P quedan en pending durante la evaluación automática. Una cuenta configurada para revisar todos los envíos pasa directamente a in_review. in_review significa que debe decidir una persona. El monto queda reservado hasta la aprobación. Si se rechazan, pasan a cancelled y el monto vuelve al remitente.

Los repartos automáticos entre cuentas también pueden quedar in_review. Si los P2P están deshabilitados, el reparto se reserva para revisión. Su configuración pasa a completed al aprobarlos o cancelled al rechazarlos. Hay que resolver la revisión antes de reembolsar el pago que originó el reparto.

Solo puedes cancelar un P2P mientras esté unclaimed. Un POST exitoso no significa que el destinatario recibió el dinero. No crees otro envío para reemplazar uno pendiente o en revisión.

Retiros bancarios: cuándo terminar el flujo

Para un retiro bancario, procesa sent como éxito terminal. Algunos retiros históricos o rails con confirmación adicional pueden mostrar completed, pero no debes bloquear tu flujo esperando ese estado después de recibir sent.

TransiciónEvento webhookQué debe hacer tu integración
pending / in_review / approvedwithdrawal.create o withdrawal.updateMantener el retiro pendiente; puede requerir revisión.
sentwithdrawal.updateMarcar el retiro como exitoso y continuar tu flujo posterior.
completedwithdrawal.update (si aplica)Conservarlo como confirmación adicional; no es requisito para cerrar el flujo.
rejected / cancelledwithdrawal.updateMarcarlo como no exitoso y manejar la reversión o el reintento.

Idempotencia

Envía un header Idempotency-Key en cada POST. Si reintentas con la misma llave y el mismo cuerpo, recibirás la respuesta original sin crear un segundo movimiento.

curl -X POST https://app.recurrente.com/api/transfers \
-H "X-SECRET-KEY: sk_live_…" \
-H "Idempotency-Key: pago-nomina-2026-07" \
-H "Content-Type: application/json" \
-d '{"amount_in_cents": 50000, "currency": "GTQ", "destination": "ba_abc123"}'

Listar y filtrar

GET /api/transfers devuelve todos tus movimientos — retiros, p2p y stablecoin — del más reciente al más antiguo. Filtra por tipo de destino con types[]:

GET /api/transfers?types[]=bank_account&types[]=crypto_address

GET /api/transfers/{id} y POST /api/transfers/{id}/cancel aceptan cualquier ID de movimiento (tr_, wi_, sw_).

Cuentas conectadas

Si operas una plataforma con cuentas conectadas, pasa account_id para mover dinero de una cuenta hija verificada. Las cuentas hijas admiten destinos bank_account, y transferencias dentro de su misma plataforma — a la cuenta madre (comisiones) o a cuentas hermanas (liquidaciones de marketplace). Los destinos fuera de la familia, teléfonos y cripto no están disponibles para cuentas hijas:

POST /api/transfers
{
"account_id": "ac_hija123",
"amount_in_cents": 50000,
"destination": "ba_delahija"
}

Webhooks

Cada tipo de movimiento emite webhooks al avanzar: transfer.sent/transfer.received (p2p), transfer.pending/transfer.in_review/transfer.cancelled (revisión de p2p), withdrawal.create/withdrawal.update (retiros) y swap.create/swap.update (stablecoin). Los eventos de retiros y stablecoin incluyen una llave transfer con la misma forma unificada que devuelve la API, además de su llave histórica (withdrawal/swap). Si integras con el endpoint unificado, lee siempre payload.transfer. En un retiro, usa el status del payload y cierra el flujo al recibir sent; no esperes un evento completed posterior.

Requisitos

Para mover dinero por API necesitas:

  1. Una llave API con movimiento de dinero habilitado (Configuración → Llaves API).
  2. Tu cuenta verificada (KYC completado).
  3. Para stablecoin: haber completado la verificación de stablecoin en la app.

¿Y los endpoints de retiros?

/api/withdrawals y los retiros anidados de cuentas conectadas siguen funcionando indefinidamente para integraciones existentes, pero están marcados como obsoletos: para integraciones nuevas usa siempre /api/transfers.