Retiros
Cobrar por API y tener que entrar al panel para llevarte el dinero al banco era la mitad del trabajo. Esto cierra el círculo.
La regla que gobierna todo
La API no puede inventarse un destino. Sólo envía a cuentas que ya están dadas de alta en tu panel, verificadas por nosotros y registradas con el proveedor.
No es una limitación provisional. Un retiro es la única llamada de la API que no se deshace: el dinero sale y no vuelve. Con destinos abiertos, un fallo de tu integración —o una credencial robada— manda tu saldo a cualquier parte. Con destinos cerrados, lo peor que puede pasar es que acabe en una cuenta que ya habías aprobado.
Para ti tiene una consecuencia buena: el cuerpo de un retiro no lleva ninguna CLABE. No puedes equivocarte al teclearla porque no la tecleas.
:::warning El alcance no viene marcado
withdrawals:write es el único alcance que el panel no marca por defecto al
emitir una credencial. Hay que marcarlo a mano, y conviene que la credencial que
lo lleve no haga nada más.
:::
1 · Mira a dónde puedes enviar
curl https://api.binkio.com/v1/payout_destinations \
-H "Authorization: Bearer bk_live_sec_…"
{
"object": "list",
"data": [
{
"object": "payout_destination",
"id": "d41f8a2c-9b73-4e15-a6c8-3f0e7b2d1a94",
"rail": "spei",
"alias": "Cuenta principal",
"beneficiary_name": "Tienda Ejemplo SA de CV",
"bank_name": "BBVA México",
"last_four": "3456",
"currency": "MXN",
"usable": true,
"created_at": "2026-07-02T09:14:03.000Z"
}
],
"has_more": false,
"next_cursor": null
}
Mira usable antes de ordenar nada. Una cuenta recién dada de alta está
esperando al proveedor: ordenar contra ella falla más adelante, con el saldo ya
apartado.
Del número de cuenta se publican sólo los últimos cuatro dígitos. Sirven para
reconocerla en una pantalla; para ordenar el retiro se usa el id.
2 · Ordena el retiro
curl -X POST https://api.binkio.com/v1/withdrawals \
-H "Authorization: Bearer bk_live_sec_…" \
-H "Idempotency-Key: liquidacion-2026-W36" \
-H "Content-Type: application/json" \
-d '{
"destination_id": "d41f8a2c-9b73-4e15-a6c8-3f0e7b2d1a94",
"amount": "5000.00",
"concept": "Liquidacion semana 36"
}'
Tres campos. El riel —SPEI o transferencia internacional— lo decide el destino, no tú.
:::danger La clave de idempotencia importa más aquí que en ningún otro sitio Esta llamada no se deshace. Usa un identificador tuyo y estable —el número de la orden de pago, el del lote—, nunca un aleatorio. Un aleatorio convierte cada reintento en un retiro nuevo, y aquí eso significa mandar el dinero dos veces. :::
Quién paga la comisión
fee_mode | Pides 1 000 | Llega | Sale de tu saldo |
|---|---|---|---|
net_out (por defecto) | 1 000 | 1 000 − comisión | 1 000 |
gross_up | 1 000 | 1 000 exactos | 1 000 + comisión |
Para pagarle a un proveedor casi siempre quieres gross_up: la factura es de
1 000 y tiene que recibir 1 000.
3 · Sigue el desenlace
La respuesta llega en processing. Eso no es un fallo: el dinero está en
camino. Un SPEI tarda minutos; una transferencia internacional, días.
curl https://api.binkio.com/v1/withdrawals/9f3b1c7e-… \
-H "Authorization: Bearer bk_live_sec_…"
| Estado | Qué significa |
|---|---|
processing | Aceptado y en camino |
completed | El dinero llegó al destino |
failed | No salió. El saldo volvió a tu cuenta |
cancelled | Cancelado antes de salir |
Lo normal es no sondear: suscríbete a withdrawal.completed y
withdrawal.failed. → Webhooks
Para conciliar
amountes lo que salió de tu saldo. Es la cifra que cuadra tu libro.net_amountes lo que recibió el destino. La diferencia es la comisión.receipt_urltrae el CEP, el comprobante oficial de Banxico, en cuanto el proveedor lo publica. Es lo que vale como prueba ante quien cobró — guárdalo.
Errores
| Código | HTTP | Qué hacer |
|---|---|---|
payout_destination_not_found | 404 | Ese destino no es tuyo. Sácalo de la lista |
payout_destination_not_usable | 409 | Todavía sin verificar. Mira usable |
payout_destination_rail_unsupported | 409 | Riel no cubierto por la API |
wallet_insufficient_balance | 409 | No alcanza para el importe más la comisión |
below_minimum | 400 | El importe no llega al mínimo del riel |
invalid_request | 400 | Supera un límite tuyo, o el riel lo rechaza. El message es lo único que lo distingue |
:::note Una aspereza que preferimos contarte
Los rechazos por límite (por operación, diario, mensual) todavía no traen un
code propio: llegan como invalid_request con el motivo en el message.
Es del riel de retiros, no del contrato, y está en la lista para arreglarse.
Mientras tanto, no ramifiques sobre el texto — trátalos como «no se pudo, mira
el mensaje».
:::
Retiros en cadena
Enviar a una dirección de blockchain también está en la API, pero es otra llamada y otro alcance:
curl -X POST https://api.binkio.com/v1/withdrawals/crypto \
-H "Authorization: Bearer bk_live_sec_…" \
-H "Idempotency-Key: envio-2026-W37-3" \
-H "Content-Type: application/json" \
-d '{
"asset": "USDC",
"amount": "500.00",
"address": "0x9c8b7e1d3a5f6c2b0a9d8e7f6a5b4c3d2e1f0a9b",
"network": "ETHEREUM"
}'
:::danger Esto no se deshace, y no hay a quién reclamar El destino va en el cuerpo y no se valida contra ninguna lista: no hay direcciones dadas de alta que comprobar. Una dirección equivocada, o la red equivocada, pierde el dinero para siempre — no lo recupera soporte, ni el proveedor, ni nosotros.
Valida la dirección en tu lado antes de llamar, y no la construyas concatenando cadenas. :::
Por qué lleva alcance propio
withdrawals:crypto está separado de withdrawals:write a propósito. Si el
destino no se puede acotar, se acota la credencial: una clave que paga
proveedores por transferencia no puede además mandar cripto a cualquier
parte. Quien necesite las dos cosas marca las dos casillas, a sabiendas.
network es obligatorio y no tiene valor por defecto
El mismo activo vive en varias redes y la dirección no dice en cuál. Deducirlo por ti sería acertar casi siempre y perder el dinero el resto de las veces.
Sácalo de GET /v1/deposit_instructions o de tu propia
configuración — la API lo publica y lo acepta en la misma forma, en mayúsculas.
La Regla de Viaje
A partir de cierto importe hace falta beneficiary (FATF R.16):
"beneficiary": {
"first_name": "Ana", "last_name": "Ruiz", "country": "MX",
"vasp_name": "Binance", "vasp_country": "MT"
}
vasp_name y vasp_country sólo si el destino es otra plataforma; se omiten
para una cartera propia. Mándalos antes: por encima del umbral el proveedor
rechaza el envío, y para entonces tu saldo ya está apartado.
Lo que se sigue haciendo desde el panel
Dar de alta un destino bancario. Es la restricción del principio, y es deliberada: es lo que hace que un fallo de tu integración no pueda mandar el saldo a una cuenta que nadie aprobó.