Referencia de la API de Vio eSIM
Dos interfaces REST sobre el mismo backend: la API orientada al cliente que impulsa la aplicación web y nuestras apps de iOS/Android, y una API de Socios independiente, autenticada por clave API, para revender Vio eSIM de forma programática.
https://vioesim.com/api/v1https://vioesim.com/api/public/v1Autenticación de la app móvil
La app web y las apps nativas comparten un mismo sistema de sesión — una app nativa simplemente lleva el token consigo en lugar de depender de una cookie.
Regístrate o inicia sesión. Ambos endpoints devuelven un campo token junto al objeto de usuario (y una cabecera Set-Cookie que usa la app web en su lugar). Guarda token en el Llavero (iOS) o en el almacenamiento respaldado por Keystore (Android).
Envíalo de vuelta en cada solicitud:
Authorization: Bearer <token>Duración del token: 30 días desde su emisión, o hasta que se llame a /api/v1/auth/logout. No hay paso de token de actualización — un token próximo a expirar simplemente se reemplaza pidiendo al usuario que inicie sesión de nuevo.
Autenticación de la API de Socios
Para empresas externas que revenden Vio eSIM a través de su propio sitio o app. Un socio es una cuenta normal de Vio eSIM: genera una clave desde Panel → Claves API (se muestra una sola vez — guárdala, no podrá volver a mostrarse), y luego envíala en cada solicitud:
Authorization: Bearer vio_live_sk_...
# or
X-API-Key: vio_live_sk_...Generar una nueva clave invalida inmediatamente la anterior.
Inicio rápido — API de Socios
Explora el catálogo y compra una eSIM en dos llamadas:
curl https://vioesim.com/api/public/v1/destinations \
-H "Authorization: Bearer vio_live_sk_..."
curl -X POST https://vioesim.com/api/public/v1/orders \
-H "Authorization: Bearer vio_live_sk_..." \
-H "Content-Type: application/json" \
-d '{ "planId": "cus...", "quantity": 1 }'Errores y límites de frecuencia
La API Móvil/Web devuelve { "error": "mensaje" } (mensajes actualmente en turco). La API de Socios devuelve una forma estructurada para que puedas ramificar según code:
{ "error": { "code": "INSUFFICIENT_BALANCE", "message": "Insufficient wallet balance." } }| Estado | Significado |
|---|---|
| 401 | Token o clave API faltante/inválido/expirado |
| 402 | Solo API de Socios — saldo del monedero demasiado bajo |
| 404 | Recurso no encontrado, o no pertenece al llamador |
| 409 | Estado conflictivo (p. ej. eliminar una cuenta con saldo distinto de cero) |
| 429 | Límite de frecuencia alcanzado — ver cabecera Retry-After (segundos) |
| 502 | Pago capturado pero falló el aprovisionamiento de la eSIM — no reintentes el cobro |
Límites de frecuencia (API de Socios): 120 solicitudes/min por clave en lecturas, 30 solicitudes/min en POST /orders.
API Móvil / Web
Autenticación
Registro
Crea una cuenta y una sesión activa.
{
"firstName": "Ada",
"lastName": "Lovelace",
"email": "[email protected]",
"password": "min 8 chars"
}{
"success": true,
"user": {
"id", "firstName", "lastName", "fullName", "email",
"emailVerified", "walletBalance", "currency", "locale", "createdAt"
},
"token": "..."
}Inicio de sesión
Cuerpo: { email, password }. Misma forma de respuesta que Registro. 401 con credenciales incorrectas, 403 si la cuenta está suspendida.
Cerrar sesión
Sin cuerpo. Elimina la sesión en el servidor — llama esto en un cierre de sesión real, no solo "olvidar el token localmente", para que un token robado no pueda seguir funcionando.
Usuario actual
Devuelve { "user": null } (nunca un error) al estar desconectado — úsalo al iniciar la app para decidir si mostrar la pantalla de inicio de sesión.
Olvidé mi contraseña
Cuerpo: { email, locale }. Siempre devuelve { success: true } exista o no la dirección, para que no pueda usarse para enumerar cuentas. Envía un correo con enlace/token de restablecimiento (expira en 1 hora).
Restablecer contraseña
Cuerpo: { token, newPassword }. Un GET con ?token= comprueba la validez antes de mostrar el formulario ({ "valid": true|false }).
Cambiar contraseña
Cuerpo: { currentPassword, newPassword }. currentPassword es obligatorio salvo que la cuenta aún no tenga contraseña (p. ej. inicio de sesión social).
Cuenta
Actualizar perfil
Envía solo los campos que quieras cambiar.
| Field | Notes |
|---|---|
| firstName, lastName | opcional · texto |
| preferredCurrency | opcional · uno de USD EUR GBP TRY |
| locale | opcional · código de 2 letras, p. ej. es |
{ "success": true, "user": { ...same shape as /auth/me } }Actualizar avatar
Cuerpo: { "avatarUrl": "data:image/..." } — un URI de datos en base64, máx. ~1,5 MB.
Generar clave API
Emite una nueva clave API de Socios para esta cuenta, reemplazando cualquier anterior. La clave sin procesar se muestra una sola vez, en esta respuesta — después solo se puede recuperar su prefijo.
{
"success": true,
"key": "vio_live_sk_...",
"apiKeyPrefix": "vio_live_sk_ab12…9f8e",
"apiKeyGeneratedAt": "2026-08-22T17:00:00.781Z"
}Eliminar cuenta
Requerido para la revisión de App Store (las apps con creación de cuentas deben ofrecer eliminación dentro de la app). Anonimiza la cuenta (correo codificado y liberado para un nuevo registro, nombre/avatar/contraseña borrados) en lugar de eliminar filas de forma permanente, para que los pedidos anteriores permanezcan en los registros fiscales/contables. Destruye todas las sesiones y dispositivos push registrados del usuario.
Catálogo
Listar destinos
Países y regiones con planes activos, con precio y formateados para UI (esto es lo que llama la propia tienda). Opcional ?filter=popular|regional|global|<texto de búsqueda>.
Detalle de destino
Detalle completo (descripción, planes) de un país por código ISO, p. ej. /api/v1/destinations/jp.
Compras
Pago
Compra un plan directamente (en lugar de depositar antes en el monedero).
| Field | Notes |
|---|---|
| planId* | |
| quantity | opcional · 1–10, por defecto 1 |
| paymentMethod* | wallet | stripe | crypto |
| successUrl, cancelUrl | opcional · para redirecciones de stripe/crypto. Usa un enlace universal/de app, no una URL web simple, para que la redirección reabra la app. |
{ "success": true, "orderId": "..." }{ "success": true, "orderId": "...", "url": "https://checkout.stripe.com/..." }Recarga de monedero con Stripe
Recarga el monedero con tarjeta — un flujo separado del pago. Cuerpo: { amount (centavos, mín. 200), currency, successUrl, cancelUrl }. Devuelve { sessionId, url }.
Recarga de monedero con cripto
Cuerpo: { amount (cadena decimal, mín. $2), currency, url_return }. Devuelve el objeto de factura de Cryptomus, incluyendo url.
Listar pedidos
Todos los pedidos del llamador, del más reciente al más antiguo.
Detalle de pedido
Un pedido, incluyendo esimIds — obtén el detalle completo de la eSIM (QR, código de activación) mediante GET /api/v1/user/esims y haz coincidir por id.
Mis eSIM
Cada eSIM que posee el llamador: iccid, activationCode, qrCodeUrl, smdpAddress, status, dataUsage/totalVolume (MB), activatedAt, expiresAt. Esto alimenta la pantalla "Mis eSIM" y la visualización del QR.
Historial del monedero
Historial de depósitos/retiros/compras/reembolsos del saldo del monedero mostrado en /auth/me.
Soporte
Listar tickets
Los tickets del llamador con sus hilos de mensajes completos, del más reciente al más antiguo.
Nuevo ticket
Cuerpo: { subject, category, message } (category opcional).
Responder a un ticket
Cuerpo: { message }. Responder a un ticket RESOLVED/CLOSED lo reabre automáticamente.
Dispositivos de notificaciones push
Solo registra tokens de dispositivo — enviar realmente requiere credenciales APNs/FCM, ver el aviso abajo.
Registrar dispositivo
Llamar tras obtener un token APNs o FCM. Cuerpo: { platform: "IOS"|"ANDROID", pushToken, appVersion }. Hace upsert por token, así que volver a llamarlo en cada inicio de la app es seguro y recomendable (los tokens pueden rotar).
Anular registro de dispositivo
Cuerpo: { pushToken }. Llamar al cerrar sesión para que un dispositivo compartido/reiniciado deje de recibir notificaciones de otro usuario.
API de Socios
Todo lo siguiente requiere la cabecera de clave API descrita arriba — aquí no aplica ninguna cookie de sesión.
Catálogo
Listar destinos
Catálogo completo, en una forma orientada a socios (no la forma de UI de la tienda).
{
"countries": [
{
"code": "JP",
"name": { "en": "Japan", "tr": "Japonya", "...": "..." },
"flagEmoji": "🇯🇵",
"continent": "Asia",
"plans": [
{ "id": "cus...", "dataAmountMb": 1024, "durationDays": 7, "priceUsd": 4.25, "isTopUp": false }
]
}
],
"regions": [ { "code": "...", "name": {...}, "countryCount": 15, "plans": [...] } ]
}Detalle de destino
Un país por código ISO (p. ej. /api/public/v1/destinations/JP), misma forma de planes que arriba.
Pedidos
Crear pedido
Compra un plan contra el saldo del monedero del llamador y aprovisiona la eSIM de forma sincrónica — la respuesta ya contiene los datos de QR/activación, listos para entregar a tu cliente final. No se necesita sondeo en el caso normal.
{ "planId": "cus...", "quantity": 1 }{
"id": "...", "orderNumber": "VIO-API-...", "status": "COMPLETED", "amountUsd": 4.25,
"esims": [
{ "id": "...", "iccid": "...", "activationCode": "...",
"qrCodeUrl": "https://...", "smdpAddress": "...", "status": "PENDING" }
]
}Listar pedidos
Opcional ?limit= (por defecto 25, máx. 100). Forma resumida — usa el endpoint de detalle para datos de eSIM.
Detalle de pedido
Detalle completo del pedido incluyendo los datos de QR/activación y el uso en vivo (dataUsageMb / totalVolumeMb) de cada eSIM — sondea esto para mostrar a tu cliente sus datos restantes.
Monedero
Saldo del monedero
Sondea antes de realizar un lote grande de pedidos, o recibe una alerta cuando esté bajo.
{ "balanceUsd": 128.40, "currency": "USD" }Lo que aún no está construido
Para que nada aquí se asuma funcional sin estarlo.
Envío de notificaciones push
El registro de tokens funciona; el envío real requiere credenciales APNs/FCM.
Precios específicos por socio
La API de Socios actualmente cobra el mismo precio de venta al público que la tienda. Un precio mayorista/negociado requeriría un cambio de esquema y una decisión de negocio.
Tokens de actualización
Las sesiones son tokens planos de larga duración (30 días), no pares de token de acceso corto + token de actualización. Suficiente por ahora; revisar más adelante para una postura de seguridad más estricta.
Pantalla de pago nativa de Stripe
La integración de Stripe usa Checkout alojado (redirección/webview). Una entrada de tarjeta nativa usaría en su lugar PaymentIntents + el SDK de Stripe.
Webhooks para socios
Los socios deben sondear GET /orders/:id para el estado; aún no hay un webhook saliente (p. ej. "eSIM activada").
