Conversiones
Cobras en pesos y quieres dólares. O al revés. Sin esto, cobro y retiro estaban automatizados y el paso de en medio era una pantalla web — en cada liquidación.
Dos llamadas, no una
Cotizar y ejecutar van separadas porque el precio caduca. Cotizar consulta al proveedor y reserva una tasa unos minutos; ejecutar la consume.
Si fuera una sola llamada, convertirías a un precio que nunca viste. En algo que no se deshace, eso es exactamente lo que no puede pasar.
1 · Pide precio
curl -X POST https://api.binkio.com/v1/conversions/quote \
-H "Authorization: Bearer bk_live_sec_…" \
-H "Idempotency-Key: cot-2026-W37-1" \
-H "Content-Type: application/json" \
-d '{ "from_asset": "MXNB", "to_asset": "USDC", "amount": "30000.00" }'
{
"object": "conversion_quote",
"quote_id": "cq_7f3a2b9c1d4e8a6f",
"from_amount": "30000.00000000",
"to_amount": "1738.52000000",
"exchange_rate": "17.25000000",
"fee": "17.39000000",
"expires_at": "2026-09-08T14:32:10.000Z",
"off_market": false
}
to_amount ya trae la comisión descontada. Es lo que entra de verdad en tu
cuenta, no el bruto — enséñale ese número a tu usuario.
:::note Cotizar también lleva Idempotency-Key
La regla es que todo POST de la API la lleva, sin excepciones: un contrato
uniforme se equivoca menos que uno con matices.
La consecuencia hay que saberla: repetir la misma clave devuelve la cotización guardada, no una nueva. Para pedir precio otra vez, clave nueva. Así un reintento por corte de red no consume otra cotización. :::
off_market
Cuando viene en true, el mercado del par está cerrado —fin de semana,
festivo— y la tasa es peor de lo habitual.
Enséñalo. Es la única explicación de por qué el precio de un domingo por la noche no es el del martes por la mañana, y sin él tu usuario lo lee como un error tuyo.
2 · Ejecútala
curl -X POST https://api.binkio.com/v1/conversions \
-H "Authorization: Bearer bk_live_sec_…" \
-H "Idempotency-Key: conv-2026-W37-1" \
-H "Content-Type: application/json" \
-d '{
"quote_id": "cq_7f3a2b9c1d4e8a6f",
"from_asset": "MXNB",
"to_asset": "USDC",
"amount": "30000.00",
"max_slippage_bps": 50
}'
El par y el importe van otra vez, y no es redundancia. Se contrastan con lo
que guardamos al cotizar: si no cuadran, se rechaza. Es lo que impide que
reusar por error un quote_id de otra conversión cambie algo distinto de lo
que aceptaste.
max_slippage_bps
Cuánto puede empeorar la tasa entre cotizar y ejecutar, en puntos básicos (100 bps = 1 %).
Entre las dos llamadas pasa tiempo real y el mercado se mueve. Sin tope, una ejecución tardía se liquida a un precio que nadie aceptó; con tope, falla y vuelves a cotizar. Pon uno.
3 · Sigue el desenlace
La respuesta puede llegar en processing, y eso no es un fallo: algunas
rutas tienen dos patas —vender y emitir— que no terminan en la misma llamada.
| Estado | Qué significa |
|---|---|
processing | En marcha |
completed | El saldo ya está en el activo de destino |
failed | No se convirtió |
Cuando falla, failure_reason dice qué paso falló. Importa: saber si se
cayó la venta o la emisión es lo que decide si tiene sentido reintentar.
Suscríbete a conversion.completed en lugar de sondear. → Webhooks
Errores
| Código | HTTP | Qué pasó |
|---|---|---|
wallet_insufficient_balance | 409 | No alcanza el saldo del activo de origen |
below_minimum | 400 | El importe no llega al mínimo de la ruta |
invalid_request | 400 | La cotización no existe, ya se usó, caducó, o el par no cuadra |
:::warning Una aspereza que preferimos contarte
Los cuatro casos de cotización —no existe, ya usada, caducada, par distinto—
llegan todos como invalid_request, con el motivo sólo en el message. El
servicio de conversiones rechaza con texto y sin código propio.
Es del riel, no del contrato, y está para arreglarse. Mientras tanto: no ramifiques sobre el texto. Trátalo como «esta cotización no sirve, pide otra». :::