Vio eSIM
API REST · v1

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.

API Mobile / Web
https://vioesim.com/api/v1
API de Parceiros
https://vioesim.com/api/public/v1

Autenticaçã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.

Uma sessão em uma conta suspensa, banida ou excluída para de funcionar imediatamente — verificado no servidor a cada requisição, mesmo que o token em si ainda não tenha expirado.

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.

Modelo de cobrança: Pedidos da API de Parceiros são cobrados de forma síncrona contra o saldo pré-pago da conta — sem redirecionamento de pagamento, já que é uma chamada servidor a servidor. Recarregue pelo painel (cartão/cripto) antes de fazer pedidos; POST /orders retorna 402 se o saldo for insuficiente.

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." } }
StatusSignificado
401Token ou chave de API ausente/inválido/expirado
402Apenas API de Parceiros — saldo da carteira insuficiente
404Recurso não encontrado, ou não pertence ao chamador
409Estado conflitante (ex. excluir uma conta com saldo diferente de zero)
429Limite de taxa atingido — veja o cabeçalho Retry-After (segundos)
502Pagamento 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

POST/api/v1/auth/registerSem autenticação

Registrar

Cria uma conta e uma sessão ativa.

Request body
{
  "firstName": "Ada",
  "lastName": "Lovelace",
  "email": "[email protected]",
  "password": "min 8 chars"
}
200 OK
{
  "success": true,
  "user": {
    "id", "firstName", "lastName", "fullName", "email",
    "emailVerified", "walletBalance", "currency", "locale", "createdAt"
  },
  "token": "..."
}
POST/api/v1/auth/loginSem autenticação

Entrar

Corpo: { email, password }. Mesmo formato de resposta do Registro. 401 em credenciais inválidas, 403 se a conta estiver suspensa.

POST/api/v1/auth/logoutToken de sessão

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.

GET/api/v1/auth/meToken de sessão · opcional

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.

POST/api/v1/auth/forgot-passwordSem autenticação

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).

POST/api/v1/auth/reset-passwordSem autenticação

Redefinir senha

Corpo: { token, newPassword }. Um GET com ?token= verifica a validade antes de exibir o formulário ({ "valid": true|false }).

POST/api/v1/auth/change-passwordToken de sessão

Alterar senha

Corpo: { currentPassword, newPassword }. currentPassword é obrigatório a menos que a conta ainda não tenha senha (ex. login social).

Conta

PATCH/api/v1/user/profileToken de sessão

Atualizar perfil

Envie apenas os campos que deseja alterar.

FieldNotes
firstName, lastNameopcional · texto
preferredCurrencyopcional · um de USD EUR GBP TRY
localeopcional · código de 2 letras, ex. pt
200 OK
{ "success": true, "user": { ...same shape as /auth/me } }
POST/api/v1/user/avatarToken de sessão

Atualizar avatar

Corpo: { "avatarUrl": "data:image/..." } — um URI de dados base64, máx. ~1,5 MB.

POST/api/v1/user/api-key/generateToken de sessão

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.

200 OK
{
  "success": true,
  "key": "vio_live_sk_...",
  "apiKeyPrefix": "vio_live_sk_ab12…9f8e",
  "apiKeyGeneratedAt": "2026-08-22T17:00:00.781Z"
}
DELETE/api/v1/user/accountToken de sessão

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.

Retorna 409 se walletBalance > 0 — o app deve orientar o usuário a sacar ou contatar o suporte primeiro, em vez de perder os fundos silenciosamente.

Catálogo

GET/api/v1/destinationsSem autenticação

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>.

GET/api/v1/destinations/:codeSem autenticação

Detalhe do destino

Detalhe completo (descrição, planos) de um país por código ISO, ex. /api/v1/destinations/jp.

Compras

POST/api/v1/checkoutToken de sessão

Checkout

Compra um plano diretamente (em vez de depositar antes na carteira).

FieldNotes
planId*
quantityopcional · 1–10, padrão 1
paymentMethod*wallet | stripe | crypto
successUrl, cancelUrlopcional · para redirecionamentos stripe/crypto. Use um link universal/de app, não uma URL web simples, para que o redirecionamento reabra o app.
200 OK — wallet (synchronous)
{ "success": true, "orderId": "..." }
200 OK — stripe / crypto (redirect required)
{ "success": true, "orderId": "...", "url": "https://checkout.stripe.com/..." }
Abra url em um navegador integrado (SFSafariViewController / Chrome Custom Tabs), não em uma WebView simples — tanto Stripe quanto Cryptomus esperam um contexto real de navegador.
POST/api/v1/payments/stripe/create-sessionToken de sessão

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 }.

POST/api/v1/payments/cryptomus/create-invoiceToken de sessão

Recarga de carteira via cripto

Corpo: { amount (string decimal, mín. $2), currency, url_return }. Retorna o objeto de fatura da Cryptomus, incluindo url.

GET/api/v1/user/ordersToken de sessão

Listar pedidos

Todos os pedidos do chamador, do mais recente ao mais antigo.

GET/api/v1/user/orders/:idToken de sessão

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.

GET/api/v1/user/esimsToken de sessão

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.

GET/api/v1/user/wallet/transactionsToken de sessão

Histórico da carteira

Histórico de depósitos/saques/compras/reembolsos do saldo da carteira exibido em /auth/me.

Suporte

GET/api/v1/support/ticketsToken de sessão

Listar tickets

Os tickets do chamador com seus fios de mensagens completos, do mais recente ao mais antigo.

POST/api/v1/support/ticketsToken de sessão

Novo ticket

Corpo: { subject, category, message } (category opcional).

POST/api/v1/support/tickets/:id/messagesToken de sessão

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.

POST/api/v1/user/push-devicesToken de sessão

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).

DELETE/api/v1/user/push-devicesToken de sessão

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

GET/api/public/v1/destinationsChave de API

Listar destinos

Catálogo completo, em um formato orientado a parceiros (não o formato de UI da loja).

200 OK
{
  "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": [...] } ]
}
GET/api/public/v1/destinations/:codeChave de API

Detalhe do destino

Um país por código ISO (ex. /api/public/v1/destinations/JP), mesmo formato de planos acima.

Pedidos

POST/api/public/v1/ordersChave de API

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.

Request body
{ "planId": "cus...", "quantity": 1 }
201 Created
{
  "id": "...", "orderNumber": "VIO-API-...", "status": "COMPLETED", "amountUsd": 4.25,
  "esims": [
    { "id": "...", "iccid": "...", "activationCode": "...",
      "qrCodeUrl": "https://...", "smdpAddress": "...", "status": "PENDING" }
  ]
}
Modos de falha: 402 INSUFFICIENT_BALANCE (recarregue primeiro), 404 PLAN_NOT_FOUND, ou 502 FULFILLMENT_FAILED (cobrado, mas o provedor não conseguiu emitir a eSIM — o id do pedido está incluído, o suporte é notificado automaticamente; faça polling em GET /orders/:id em vez de cobrar novamente).
GET/api/public/v1/ordersChave de API

Listar pedidos

Opcional ?limit= (padrão 25, máx. 100). Formato resumido — use o endpoint de detalhe para dados de eSIM.

GET/api/public/v1/orders/:idChave de API

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

GET/api/public/v1/walletChave de API

Saldo da carteira

Faça polling antes de um grande lote de pedidos, ou alerte-se quando estiver baixo.

200 OK
{ "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").

Referência da API Vio eSIM e documentação da Partner API