Referência da API Vio eSIM
Duas superfícies REST sobre o mesmo backend: a API voltada ao cliente que alimenta o app web e nossos apps iOS/Android, e uma API de Parceiros separada, autenticada por chave de API, para revender Vio eSIM programaticamente.
https://vioesim.com/api/v1https://vioesim.com/api/public/v1Autenticação do app mobile
O app web e os apps nativos compartilham um único sistema de sessão — um app nativo simplesmente carrega o próprio token em vez de depender de um cookie.
Registre-se ou entre. Ambos os endpoints retornam um campo token junto ao objeto de usuário (e um cabeçalho Set-Cookie que o app web usa em vez disso). Armazene token no Keychain (iOS) ou no armazenamento apoiado pelo Keystore (Android).
Envie-o de volta em cada requisição:
Authorization: Bearer <token>Validade do token: 30 dias a partir da emissão, ou até que /api/v1/auth/logout seja chamado. Não há etapa de refresh token — um token prestes a expirar é simplesmente substituído pedindo ao usuário para entrar novamente.
Autenticação da API de Parceiros
Para empresas externas que revendem Vio eSIM através do próprio site ou app. Um parceiro é uma conta Vio eSIM normal: gere uma chave em Painel → Chaves de API (exibição única — guarde-a, não poderá ser exibida novamente), depois envie-a em cada requisição:
Authorization: Bearer vio_live_sk_...
# or
X-API-Key: vio_live_sk_...Gerar uma nova chave invalida imediatamente a anterior.
Início rápido — API de Parceiros
Explore o catálogo e compre uma eSIM em duas chamadas:
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 }'Erros e limites de taxa
A API Mobile/Web retorna { "error": "mensagem" } (mensagens atualmente em turco). A API de Parceiros retorna um formato estruturado para você tratar por code:
{ "error": { "code": "INSUFFICIENT_BALANCE", "message": "Insufficient wallet balance." } }| Status | Significado |
|---|---|
| 401 | Token ou chave de API ausente/inválido/expirado |
| 402 | Apenas API de Parceiros — saldo da carteira insuficiente |
| 404 | Recurso não encontrado, ou não pertence ao chamador |
| 409 | Estado conflitante (ex. excluir uma conta com saldo diferente de zero) |
| 429 | Limite de taxa atingido — veja o cabeçalho Retry-After (segundos) |
| 502 | Pagamento capturado mas o provisionamento da eSIM falhou — não repita a cobrança |
Limites de taxa (API de Parceiros): 120 req/min por chave em leituras, 30 req/min em POST /orders.
API Mobile / Web
Autenticação
Registrar
Cria uma conta e uma sessão ativa.
{
"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": "..."
}Entrar
Corpo: { email, password }. Mesmo formato de resposta do Registro. 401 em credenciais inválidas, 403 se a conta estiver suspensa.
Sair
Sem corpo. Exclui a sessão no servidor — chame isso em um logout real, não apenas "esquecer o token localmente", para que um token roubado não continue funcionando.
Usuário atual
Retorna { "user": null } (nunca um erro) quando deslogado — use isso na inicialização do app para decidir se mostra a tela de login.
Esqueci minha senha
Corpo: { email, locale }. Sempre retorna { success: true } independentemente de o endereço existir, para não permitir enumerar contas. Envia um e-mail com link/token de redefinição (expira em 1 hora).
Redefinir senha
Corpo: { token, newPassword }. Um GET com ?token= verifica a validade antes de exibir o formulário ({ "valid": true|false }).
Alterar senha
Corpo: { currentPassword, newPassword }. currentPassword é obrigatório a menos que a conta ainda não tenha senha (ex. login social).
Conta
Atualizar perfil
Envie apenas os campos que deseja alterar.
| Field | Notes |
|---|---|
| firstName, lastName | opcional · texto |
| preferredCurrency | opcional · um de USD EUR GBP TRY |
| locale | opcional · código de 2 letras, ex. pt |
{ "success": true, "user": { ...same shape as /auth/me } }Atualizar avatar
Corpo: { "avatarUrl": "data:image/..." } — um URI de dados base64, máx. ~1,5 MB.
Gerar chave de API
Emite uma nova chave de API de Parceiros para esta conta, substituindo qualquer anterior. A chave bruta é exibida apenas uma vez, nesta resposta — depois disso só o prefixo pode ser recuperado.
{
"success": true,
"key": "vio_live_sk_...",
"apiKeyPrefix": "vio_live_sk_ab12…9f8e",
"apiKeyGeneratedAt": "2026-08-22T17:00:00.781Z"
}Excluir conta
Necessário para a revisão da App Store (apps com criação de conta devem oferecer exclusão no próprio app). Anonimiza a conta (e-mail embaralhado e liberado para novo cadastro, nome/avatar/senha apagados) em vez de excluir linhas permanentemente, para que pedidos anteriores permaneçam nos registros fiscais/contábeis. Destrói todas as sessões e dispositivos push registrados do usuário.
Catálogo
Listar destinos
Países e regiões com planos ativos, precificados e formatados para UI (isso é o que a própria loja chama). Opcional ?filter=popular|regional|global|<texto de busca>.
Detalhe do destino
Detalhe completo (descrição, planos) de um país por código ISO, ex. /api/v1/destinations/jp.
Compras
Checkout
Compra um plano diretamente (em vez de depositar antes na carteira).
| Field | Notes |
|---|---|
| planId* | |
| quantity | opcional · 1–10, padrão 1 |
| paymentMethod* | wallet | stripe | crypto |
| successUrl, cancelUrl | opcional · para redirecionamentos stripe/crypto. Use um link universal/de app, não uma URL web simples, para que o redirecionamento reabra o app. |
{ "success": true, "orderId": "..." }{ "success": true, "orderId": "...", "url": "https://checkout.stripe.com/..." }Recarga de carteira via Stripe
Recarrega a carteira por cartão — um fluxo separado do Checkout. Corpo: { amount (centavos, mín. 200), currency, successUrl, cancelUrl }. Retorna { sessionId, url }.
Recarga de carteira via cripto
Corpo: { amount (string decimal, mín. $2), currency, url_return }. Retorna o objeto de fatura da Cryptomus, incluindo url.
Listar pedidos
Todos os pedidos do chamador, do mais recente ao mais antigo.
Detalhe do pedido
Um pedido, incluindo esimIds — busque o detalhe completo da eSIM (QR, código de ativação) via GET /api/v1/user/esims e associe pelo id.
Minhas eSIMs
Cada eSIM que o chamador possui: iccid, activationCode, qrCodeUrl, smdpAddress, status, dataUsage/totalVolume (MB), activatedAt, expiresAt. Isso alimenta a tela "Minhas eSIMs" e a exibição do QR.
Histórico da carteira
Histórico de depósitos/saques/compras/reembolsos do saldo da carteira exibido em /auth/me.
Suporte
Listar tickets
Os tickets do chamador com seus fios de mensagens completos, do mais recente ao mais antigo.
Novo ticket
Corpo: { subject, category, message } (category opcional).
Responder a um ticket
Corpo: { message }. Responder a um ticket RESOLVED/CLOSED o reabre automaticamente.
Dispositivos de notificação push
Apenas registra tokens de dispositivo — o envio real requer credenciais APNs/FCM, veja o aviso abaixo.
Registrar dispositivo
Chamar após obter um token APNs ou FCM. Corpo: { platform: "IOS"|"ANDROID", pushToken, appVersion }. Faz upsert pelo token, então chamar novamente a cada inicialização do app é seguro e recomendado (tokens podem rotacionar).
Cancelar registro de dispositivo
Corpo: { pushToken }. Chamar no logout para que um dispositivo compartilhado/redefinido pare de receber notificações de outro usuário.
API de Parceiros
Tudo abaixo requer o cabeçalho de chave de API descrito acima — nenhum cookie de sessão se aplica aqui.
Catálogo
Listar destinos
Catálogo completo, em um formato orientado a parceiros (não o formato de UI da loja).
{
"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": [...] } ]
}Detalhe do destino
Um país por código ISO (ex. /api/public/v1/destinations/JP), mesmo formato de planos acima.
Pedidos
Criar pedido
Compra um plano contra o saldo da carteira do chamador e provisiona a eSIM de forma síncrona — a resposta já contém os dados de QR/ativação, prontos para entregar ao seu cliente final. Nenhum polling necessário no 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= (padrão 25, máx. 100). Formato resumido — use o endpoint de detalhe para dados de eSIM.
Detalhe do pedido
Detalhe completo do pedido incluindo dados de QR/ativação e uso em tempo real (dataUsageMb / totalVolumeMb) de cada eSIM — faça polling disso para mostrar ao seu cliente os dados restantes.
Carteira
Saldo da carteira
Faça polling antes de um grande lote de pedidos, ou alerte-se quando estiver baixo.
{ "balanceUsd": 128.40, "currency": "USD" }O que ainda não foi construído
Para que nada aqui seja presumido como funcional sem estar.
Entrega de notificações push
O registro de token funciona; o envio real requer credenciais APNs/FCM.
Preços específicos por parceiro
A API de Parceiros atualmente cobra o mesmo preço de varejo da loja. Preços de atacado/negociados exigiriam uma mudança de esquema e uma decisão de negócio.
Refresh tokens
Sessões são tokens planos de longa duração (30 dias), não pares de token de acesso curto + refresh token. Suficiente por enquanto; revisar depois para uma postura de segurança mais rígida.
Tela de pagamento nativa da Stripe
A integração com a Stripe usa Checkout hospedado (redirecionamento/webview). Uma entrada de cartão nativa usaria PaymentIntents + o SDK da Stripe.
Webhooks para parceiros
Parceiros devem fazer polling em GET /orders/:id para o status; ainda não há webhook de saída (ex. "eSIM ativada").
