Convierte tu saldo en VES o COP a USDT
Convierte los bolívares o pesos colombianos que cobras con VEXPay en USDT, desde el portal o por API, cuando quieras o automáticamente con cada pago en COP. Cotiza una tasa, acéptala y recibe los USDT en tu saldo USDT de VEXPay.
Cómo funciona
Cada pago que cobras en bolívares suma a tu saldo disponible en VES (GET /v1/balance). Una conversión pasa parte de ese saldo a tu saldo USDT (GET /v1/crypto/balance) a una tasa que ves y aceptas primero. No hay transferencias bancarias y no le envías nada a VEXPay: los VES ya están en tu saldo VEXPay.
- Ten USDT habilitado
Las conversiones se activan automáticamente en toda cuenta con USDT habilitado. Sin USDT, todos los endpoints de conversión responden 403
conversions_not_enabled. - Cotiza
Pide una cotización con los VES que quieres gastar o los USDT que quieres recibir. Muestra la tasa exacta y ambos montos, y vale por 60 segundos.
- Convierte
Acepta la cotización. Los VES salen de tu saldo de inmediato: las conversiones pueden usar bolívares que siguen en la retención de 24 h, y los USDT comprados con ellos quedan retenidos hasta que esa retención termine. A menudo la conversión ya viene
COMPLETEDen la misma respuesta; si no, quedaPENDINGhasta que VEXPay entregue los USDT. Los montos quedan fijos: los movimientos del mercado no los cambian. - Recibe los USDT
VEXPay entrega los USDT en tu saldo USDT, la conversión pasa a
COMPLETEDy recibes el webhookconversion.completed. - Úsalos
Los USDT quedan en tu saldo USDT de VEXPay. Envíalos a cualquier wallet con un payout USDT (
POST /v1/crypto/payouts) cuando quieras.
Estados y tus saldos
| Estado | Qué significa | Tus VES | Tus USDT | Webhook |
|---|---|---|---|---|
| PENDING | Aceptaste una cotización; VEXPay está entregando los USDT (no ocurre si se completa en la misma petición) | Ya debitados | Aún no acreditados | — (la respuesta del POST) |
| COMPLETED | Los USDT se entregaron (final) | Debitados | Acreditados con targetAmountUsdt | conversion.completed |
| CANCELED | La cancelaste o VEXPay no pudo completarla (final) | Devueltos completos | Sin cambios | conversion.canceled |
Ejemplo con spread de 2% y tasa de mercado 979,2450 (tasa 998,8299), convirtiendo 10.000,00 VES:
| Momento | availableVes | convertedVes | USDT disponible |
|---|---|---|---|
| Antes | 25.000,00 | 0,00 | 0,00 |
| Después de POST /v1/conversions (PENDING) | 15.000,00 | 10.000,00 | 0,00 |
| Después de conversion.completed | 15.000,00 | 10.000,00 | 10,01 |
| …o después de conversion.canceled | 25.000,00 | 0,00 | 0,00 |
convertedVes en GET /v1/balance es el neto de VES convertidos (las conversiones canceladas se restan). Las conversiones y los payouts en VES usan el mismo saldo disponible y se debitan de forma atómica, así que nunca gastan los mismos bolívares.
La tasa y lo que cuesta
rateson VES por 1 USDT: la tasa de mercado USDT/VES actual (marketRate) más tu spread (spreadPercent). No es la tasa oficial del BCV.- El spread es todo el precio de la conversión: no hay otra comisión. Lo que muestra la cotización es exactamente lo que pagas y recibes.
- Los USDT que recibes se redondean hacia abajo al céntimo; los VES que pagas, hacia arriba.
- Si no hay una tasa de mercado actual, la cotización responde 503
rate_unavailable: reintenta en unos minutos. - Tu cuenta puede tener un mínimo por conversión (
amount_below_minimum) y un límite diario en VES (daily_limit_exceeded, contado desde las 00:00 hora de Caracas).
Ejemplo: mercado 979,2450 + 2% → tasa 998,8299. 10.000,00 VES → 10,01 USDT. 50,00 USDT → 49.941,50 VES.
Convertir COP a USDT
Los pesos que cobras con Bre-B, Nequi y Daviplata quedan en tu saldo COP (GET /v1/cop/balance). Conviértelos con los mismos endpoints: agrega "sourceCurrency": "COP" y envía sourceAmount (pesos enteros) o targetAmountUsdt. Tu cuenta necesita USDT y COP habilitados.
curl -X POST "${API_URL}/v1/conversions/quotes" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{ "sourceCurrency": "COP", "sourceAmount": "1000000" }'const quote = await vexpay.conversions.quotes.create({ sourceCurrency: 'COP', sourceAmount: '1000000' });
const conversion = await vexpay.conversions.create({ quoteId: quote.id }, { idempotencyKey: 'convert-cop-001' });quote = vexpay.conversions.quotes.create({"sourceCurrency": "COP", "sourceAmount": "1000000"})$quote = $vexpay->conversions->quotes->create(['sourceCurrency' => 'COP', 'sourceAmount' => '1000000']);{
"id": "0c7d3f2e-8a1b-4c5d-9e6f-1a2b3c4d5e6f",
"object": "conversion_quote",
"rate": "4037.6000",
"marketRate": "3920.0000",
"spreadPercent": "3.0000",
"rateSource": "market",
"sourceCurrency": "COP",
"sourceAmount": "1000000",
"sourceAmountVes": null,
"targetAmountUsdt": "247.67",
"expiresAt": "2026-10-09T15:01:00.000Z",
"createdAt": "2026-10-09T15:00:00.000Z"
}rateson COP por 1 USDT: la tasa de mercado USDT/COP más tu spread COP (puede ser distinto a tu spread VES).- Los COP que pagas se redondean hacia arriba al peso entero; los USDT que recibes, hacia abajo al centavo. Ejemplo: mercado 3.920 + 3% → tasa 4037.6000; 1.000.000 COP → 247.67 USDT; 50.00 USDT → 201.880 COP.
- Al aceptar, se debitan tus COP disponibles de inmediato. Si cancelas una conversión pendiente, los COP vuelven. El límite diario en COP se cuenta desde las 00:00 hora de Bogotá.
sourceAmountVesesnullen cotizaciones y conversiones COP; usasourceCurrencyysourceAmount.GET /v1/conversions?sourceCurrency=COPlista solo las conversiones COP.- Un reembolso COP sigue necesitando saldo COP disponible. Los pesos que ya convertiste no están disponibles para reembolsos: guarda algo de COP si esperas reembolsos.
Conversión automática de pagos COP
En lugar de cotizar tú, deja que VEXPay convierta una parte de cada pago COP al completarse. Define un porcentaje entero de 0 (apagado, por defecto) a 100. Cuando un pago COP se completa, VEXPay convierte esa parte del neto (después de la comisión COP) a la tasa actual más tu spread.
curl -X PATCH "${API_URL}/v1/conversions/settings" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{ "autoConvert": { "COP": { "percent": 50 } } }'await vexpay.conversions.settings.update({ autoConvert: { COP: { percent: 50 } } });
const settings = await vexpay.conversions.settings.retrieve();vexpay.conversions.settings.update({"autoConvert": {"COP": {"percent": 50}}})$vexpay->conversions->settings->update(['autoConvert' => ['COP' => ['percent' => 50]]]);- Ejemplo: un pago de 100.000 COP con la comisión de 4,5% + COP 1.500 deja 94.000 COP netos. Al 50% se convierten 47.000 COP (11.64 USDT a tasa 4037.6000) y 47.000 COP quedan en tu saldo COP.
- Recibes primero
payment.completedy luegoconversion.createdconorigin: "auto"y elpaymentId(normalmente en menos de un minuto). Después llegaconversion.completed, como en cualquier conversión. La conversión automática nunca retrasa ni cambia el pago. - Aplica solo a pagos completados después de activarla. Si la parte a convertir queda por debajo de tu mínimo en USDT, o superaría tu límite diario en COP, no se convierte y los pesos quedan en COP.
- Si no hay tasa de mercado, VEXPay reintenta durante 24 horas; después los pesos quedan en COP.
- Puedes cancelar una conversión automática pendiente como cualquier otra.
GET /v1/conversions/settingsmuestra también tus spreads, mínimo, límites diarios y desde qué monedas puedes convertir. Por API solo se puede cambiarautoConvert; para lo demás contacta a VEXPay.- En el portal, abre COP: la tarjeta "Auto-convert COP payments" define el mismo porcentaje.
Desde el portal (sin código)
- Abre USDT
En el portal de VEXPay, abre USDT. Con USDT habilitado verás la tarjeta "Convert VES → USDT" con tus VES disponibles y tu mínimo.
- Ingresa un monto
Elige "Spend VES" (gastar VES) o "Receive USDT" (recibir USDT), escribe el monto y pulsa Get quote.
- Confirma en 60 segundos
Revisa la tasa y ambos montos y pulsa Confirm conversion. Si se acaba el tiempo, pulsa Refresh quote.
- Síguela
La conversión aparece en el historial debajo de la tarjeta como pendiente, y luego completada (los USDT aparecen en tu saldo USDT) o cancelada (los VES vuelven). Puedes cancelarla desde ahí mientras esté pendiente.
Paso 01
Cotiza
Envía exactamente uno de sourceAmountVes (VES a gastar) o targetAmountUsdt (USDT a recibir), como texto con hasta 2 decimales.
curl -X POST "${API_URL}/v1/conversions/quotes" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "content-type: application/json" \
--data '{ "sourceAmountVes": "10000.00" }'const quote = await vexpay.conversions.quotes.create({ sourceAmountVes: '10000.00' });
// o: { targetAmountUsdt: '50.00' }quote = vexpay.conversions.quotes.create({"sourceAmountVes": "10000.00"})$quote = $vexpay->conversions->quotes->create(['sourceAmountVes' => '10000.00']);{
"id": "6f1c2b0e-4c1d-4a8e-9a51-0f4a1e9a0001",
"object": "conversion_quote",
"rate": "998.8299",
"marketRate": "979.2450",
"spreadPercent": "2.0000",
"rateSource": "market",
"sourceAmountVes": "10000.00",
"targetAmountUsdt": "10.01",
"expiresAt": "2026-10-03T15:01:00.000Z",
"createdAt": "2026-10-03T15:00:00.000Z"
}Una cotización es un precio, no una reserva: no aparta tus VES. Muéstrala a quien decide y acéptala solo cuando confirme.
Paso 02
Convierte
Acepta la cotización antes de expiresAt. Envía siempre un header Idempotency-Key (los SDKs lo envían por ti): si la petición expira, reintenta con la misma clave y recibes la misma conversión en lugar de convertir dos veces.
curl -X POST "${API_URL}/v1/conversions" \
--header "x-api-key: ${VEXPAY_API_KEY}" \
--header "Idempotency-Key: convert-2026-10-03-001" \
--header "content-type: application/json" \
--data '{ "quoteId": "6f1c2b0e-4c1d-4a8e-9a51-0f4a1e9a0001", "reference": "tesoreria-2026-10-03" }'const conversion = await vexpay.conversions.create(
{ quoteId: quote.id, reference: 'tesoreria-2026-10-03' },
{ idempotencyKey: 'convert-2026-10-03-001' },
);conversion = vexpay.conversions.create(
{"quoteId": quote.id, "reference": "tesoreria-2026-10-03"},
idempotency_key="convert-2026-10-03-001",
)$conversion = $vexpay->conversions->create(
['quoteId' => $quote->id, 'reference' => 'tesoreria-2026-10-03'],
['idempotency_key' => 'convert-2026-10-03-001'],
);{
"id": "b7e3a9d2-5f60-4c2b-8f0e-2d1c3b4a5e6f",
"object": "conversion",
"status": "PENDING",
"reference": "tesoreria-2026-10-03",
"rate": "998.8299",
"marketRate": "979.2450",
"spreadPercent": "2.0000",
"rateSource": "market",
"sourceAmountVes": "10000.00",
"targetAmountUsdt": "10.01",
"createdAt": "2026-10-03T15:00:20.000Z",
"completedAt": null,
"canceledAt": null,
"cancelReason": null
}reference es opcional y tuyo (hasta 128 caracteres): úsalo para relacionar la conversión con tu propio registro. Vuelve en cada lectura y webhook.
Paso 03
Maneja el resultado
Suscribe tu endpoint de webhooks a conversion.completed y conversion.canceled (POST /v1/webhooks o el portal), y a conversion.created si usas la conversión automática. Cada uno trae la conversión en data (con sourceCurrency, sourceAmount, origin y paymentId), más livemode, y va firmado como todos los webhooks de VEXPay.
{
"event": "conversion.completed",
"data": {
"id": "b7e3a9d2-5f60-4c2b-8f0e-2d1c3b4a5e6f",
"object": "conversion",
"status": "COMPLETED",
"reference": "tesoreria-2026-10-03",
"rate": "998.8299",
"sourceAmountVes": "10000.00",
"targetAmountUsdt": "10.01",
"completedAt": "2026-10-03T16:12:05.000Z",
"livemode": true
},
"timestamp": "2026-10-03T16:12:05.000Z"
}| Evento | Qué hacer |
|---|---|
| conversion.completed | Marca la conversión como hecha. targetAmountUsdt ya está en tu saldo USDT y puedes enviarlo. |
| conversion.created | Empezó una conversión: aceptaste una cotización, o la conversión automática convirtió parte de un pago COP (origin: "auto", paymentId). El origen ya está debitado. |
| conversion.canceled | Márcala como cancelada. sourceAmount volvió a tus VES o COP disponibles. cancelReason es canceled_by_tenant si la cancelaste tú; si no, el motivo por el que VEXPay no pudo completarla. Cotiza de nuevo si aún quieres USDT. |
Cada evento se entrega una vez y no se reintenta, así que concilia también: lista tus conversiones pendientes (GET /v1/conversions?status=PENDING) de forma periódica y consulta las que no viste terminar (GET /v1/conversions/{id}). Procesa los eventos de forma idempotente por id.
¿Cambiaste de idea? Cancela una conversión pendiente con POST /v1/conversions/{id}/cancel: los VES vuelven al instante. Una vez COMPLETED o CANCELED ya no se puede cancelar (409 conversion_not_cancelable).
Ejemplo completo
Convierte una parte de los VES del día, guarda la conversión y termínala desde el webhook.
import VexPay from '@vexpay/node';
const vexpay = new VexPay(process.env.VEXPAY_API_KEY);
// 1. Convertir (p. ej. desde un job diario)
export async function convertVes(amountVes, runId) {
const quote = await vexpay.conversions.quotes.create({ sourceAmountVes: amountVes });
console.log(`${quote.sourceAmountVes} VES → ${quote.targetAmountUsdt} USDT a ${quote.rate}`);
const conversion = await vexpay.conversions.create(
{ quoteId: quote.id, reference: runId },
{ idempotencyKey: `convert-${runId}` },
);
await db.conversions.insert({ id: conversion.id, status: conversion.status, usdt: conversion.targetAmountUsdt });
}
// 2. Terminar desde el webhook
app.post('/webhooks/vexpay', express.raw({ type: 'application/json' }), async (req, res) => {
const event = vexpay.webhooks.constructEvent(req.body, req.headers['vexpay-signature'], process.env.VEXPAY_WEBHOOK_SECRET);
if (event.event === 'conversion.completed' || event.event === 'conversion.canceled') {
await db.conversions.update(event.data.id, { status: event.data.status });
}
res.sendStatus(200);
});
// 3. Conciliar (p. ej. cada hora)
for await (const c of vexpay.conversions.list({ status: 'PENDING' })) {
await db.conversions.update(c.id, { status: c.status });
}from vexpay import VexPay, Webhook
vexpay = VexPay(os.environ["VEXPAY_API_KEY"])
def convert_ves(amount_ves: str, run_id: str):
quote = vexpay.conversions.quotes.create({"sourceAmountVes": amount_ves})
conversion = vexpay.conversions.create(
{"quoteId": quote.id, "reference": run_id},
idempotency_key=f"convert-{run_id}",
)
db.save_conversion(conversion.id, conversion.status, conversion.targetAmountUsdt)
def on_webhook(raw_body: bytes, signature: str):
event = Webhook.construct_event(raw_body, signature, os.environ["VEXPAY_WEBHOOK_SECRET"])
if event.event in ("conversion.completed", "conversion.canceled"):
db.update_conversion(event.data["id"], event.data["status"])Después de convertir: usa tus USDT
- Los USDT están en tu saldo USDT de VEXPay (
GET /v1/crypto/balance→availableUsdt), junto con los USDT que recibas de tus clientes. - Envíalos a cualquier wallet con
POST /v1/crypto/payouts(aplica la comisión de red; ver USDT y USDC). Los payouts dentro de tus límites salen automáticamente; los más grandes se revisan primero. - Una conversión nunca envía USDT a una wallet por sí sola, y los USDT nunca se convierten de vuelta a VES.
Buenas prácticas
- Cotiza justo antes de convertir. Con 410
quote_expired, pide una nueva cotización y muestra los montos nuevos: nunca aceptes una tasa que el usuario no vio. - Usa una Idempotency-Key por cada conversión que quieras hacer (p. ej. tu id de ejecución u orden) y repítela en los reintentos.
- Con 422
insufficient_balance, leeavailableVesen el error y cotiza un monto menor; payouts en VES u otras conversiones pueden haber usado el saldo. - Considera tuyos los USDT solo después de
conversion.completed. - Usa un
referencecon sentido: es la forma más fácil de cuadrar conversiones con tu contabilidad.
Errores
| Estado | error | Significado |
|---|---|---|
| 400 | invalid_amount | Envía exactamente un monto positivo con hasta 2 decimales (COP: pesos enteros). |
| 400 | invalid_data | sourceCurrency desconocido, o un campo de configuración que no puedes cambiar. |
| 403 | conversions_not_enabled | Las conversiones (o USDT, o COP para conversiones COP) no están habilitadas en tu cuenta. |
| 403 | live_mode_not_activated | Tu cuenta de producción aún no está aprobada; usa modo prueba. |
| 404 | not_found | Cotización o conversión desconocida (o de otra cuenta). |
| 409 | quote_used | Esa cotización ya fue aceptada. |
| 409 | conversion_not_cancelable | Solo se puede cancelar una conversión PENDING. |
| 410 | quote_expired | Pasaron más de 60 segundos: pide una nueva cotización. |
| 422 | amount_below_minimum | Por debajo de tu mínimo de USDT por conversión. |
| 422 | insufficient_balance | Tu saldo disponible en VES no cubre el monto. |
| 422 | daily_limit_exceeded | La conversión superaría tu límite diario en VES. |
| 503 | rate_unavailable | No hay tasa de mercado actual; reintenta en breve. |
Pruebas
Las conversiones están activas en tu cuenta de prueba siempre que USDT esté habilitado. Con una clave de prueba, las conversiones se completan al instante: se debitan los VES de prueba, se acredita tu saldo USDT de prueba y se envía conversion.completed, para que construyas y pruebes todo el flujo (incluido tu manejador de webhooks) antes de salir a producción. Usan la tasa de mercado real pero nunca mueven dinero real.
Preguntas frecuentes
| Pregunta | Respuesta |
|---|---|
| ¿La tasa es la del BCV? | No. Es la tasa de mercado USDT/VES actual más tu spread. |
| ¿Puede cambiar el monto después de aceptar? | No. Ambos montos quedan fijos al aceptar la cotización. |
| ¿Hay otras comisiones? | No. El spread es el precio de la conversión. Enviar los USDT después cuesta la comisión de red del payout. |
| ¿Puedo convertir USDT de vuelta a VES? | No. Las conversiones solo van de VES o COP a USDT; no hay off-ramp de USDT a VES. Los pagos por Pago Móvil o transferencia (POST /v1/payouts) salen solo de tu saldo en VES, que viene de los bolívares que cobras, así que los USDT no pueden financiarlos. Si necesitas un off-ramp, contacta a VEX Pay. |
| ¿VEXPay puede enviar los USDT directo a mi wallet? | No como parte de la conversión. Cuando estén en tu saldo USDT, crea un payout USDT. |
| ¿Cómo lo habilito o cambio mi spread o límites? | Contacta a VEX Pay. |