Vio eSIM
API REST · v1

Référence API Vio eSIM

Deux interfaces REST sur le même backend : l'API destinée aux clients qui alimente l'application web et nos applications iOS/Android, et une API Partenaire distincte, authentifiée par clé API, pour revendre Vio eSIM de manière programmatique.

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

Authentification de l'application mobile

L'application web et les applications natives partagent un seul système de session — une application native transporte simplement le jeton lui-même au lieu de dépendre d'un cookie.

Inscrivez-vous ou connectez-vous. Les deux points de terminaison renvoient un champ token accompagnant l'objet utilisateur (ainsi qu'un en-tête Set-Cookie utilisé par l'application web). Stockez token dans le Trousseau (iOS) ou le stockage sécurisé par Keystore (Android).

Renvoyez-le à chaque requête :

Authorization: Bearer <token>

Durée de vie du jeton : 30 jours à partir de l'émission, ou jusqu'à l'appel de /api/v1/auth/logout. Il n'y a pas d'étape de jeton de rafraîchissement — un jeton bientôt expiré est simplement remplacé en redemandant à l'utilisateur de se connecter.

Une session sur un compte suspendu, banni ou supprimé cesse de fonctionner immédiatement — vérifié côté serveur à chaque requête, même si le jeton lui-même n'a pas expiré.

Authentification de l'API Partenaire

Pour les entreprises externes qui revendent Vio eSIM via leur propre site ou application. Un partenaire est un compte Vio eSIM normal : générez une clé depuis Tableau de bord → Clés API (affichage unique — conservez-la, elle ne pourra plus être réaffichée), puis envoyez-la à chaque requête :

Authorization: Bearer vio_live_sk_...
# or
X-API-Key: vio_live_sk_...

Générer une nouvelle clé invalide immédiatement la précédente.

Modèle de facturation : Les commandes de l'API Partenaire sont facturées de manière synchrone sur le solde prépayé du compte — aucune redirection de paiement, car il s'agit d'un appel serveur à serveur. Rechargez depuis le tableau de bord (carte/crypto) avant de passer des commandes ; POST /orders renvoie 402 si le solde est insuffisant.

Démarrage rapide — API Partenaire

Parcourez le catalogue et achetez une eSIM en deux appels :

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

Erreurs et limites de débit

L'API Mobile/Web renvoie { "error": "message" } (messages actuellement en turc). L'API Partenaire renvoie une forme structurée pour que vous puissiez brancher sur code :

{ "error": { "code": "INSUFFICIENT_BALANCE", "message": "Insufficient wallet balance." } }
StatutSignification
401Jeton ou clé API manquant/invalide/expiré
402API Partenaire uniquement — solde du portefeuille trop bas
404Ressource introuvable ou non détenue par l'appelant
409État conflictuel (ex. suppression d'un compte avec un solde non nul)
429Limite de débit atteinte — voir l'en-tête Retry-After (secondes)
502Paiement capturé mais provisionnement de l'eSIM échoué — ne relancez pas le paiement

Limites de débit (API Partenaire) : 120 req/min par clé en lecture, 30 req/min sur POST /orders.

API Mobile / Web

Authentification

POST/api/v1/auth/registerAucune authentification

Inscription

Crée un compte et une session active.

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/loginAucune authentification

Connexion

Corps : { email, password }. Même forme de réponse que l'inscription. 401 en cas d'identifiants incorrects, 403 si le compte est suspendu.

POST/api/v1/auth/logoutJeton de session

Déconnexion

Pas de corps. Supprime la session côté serveur — appelez ceci lors d'une vraie déconnexion, pas seulement en "oubliant le jeton localement", afin qu'un jeton volé ne puisse pas continuer à fonctionner.

GET/api/v1/auth/meJeton de session · facultatif

Utilisateur actuel

Renvoie { "user": null } (jamais une erreur) en cas de déconnexion — utilisez ceci au lancement de l'app pour décider d'afficher l'écran de connexion.

POST/api/v1/auth/forgot-passwordAucune authentification

Mot de passe oublié

Corps : { email, locale }. Renvoie toujours { success: true } que l'adresse existe ou non, afin de ne pas permettre l'énumération des comptes. Envoie un e-mail avec un lien/jeton de réinitialisation (expire dans 1 heure).

POST/api/v1/auth/reset-passwordAucune authentification

Réinitialiser le mot de passe

Corps : { token, newPassword }. Un GET avec ?token= vérifie la validité avant d'afficher le formulaire ({ "valid": true|false }).

POST/api/v1/auth/change-passwordJeton de session

Changer le mot de passe

Corps : { currentPassword, newPassword }. currentPassword est requis sauf si le compte n'a pas encore de mot de passe (ex. connexion sociale).

Compte

PATCH/api/v1/user/profileJeton de session

Mettre à jour le profil

Envoyez uniquement les champs que vous souhaitez modifier.

FieldNotes
firstName, lastNamefacultatif · texte
preferredCurrencyfacultatif · l'un de USD EUR GBP TRY
localefacultatif · code à 2 lettres, ex. fr
200 OK
{ "success": true, "user": { ...same shape as /auth/me } }
POST/api/v1/user/avatarJeton de session

Mettre à jour l'avatar

Corps : { "avatarUrl": "data:image/..." } — un URI de données base64, max ~1,5 Mo.

POST/api/v1/user/api-key/generateJeton de session

Générer une clé API

Émet une nouvelle clé API Partenaire pour ce compte, remplaçant toute clé précédente. La clé brute n'est affichée qu'une fois, dans cette réponse — seul son préfixe reste ensuite récupérable.

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/accountJeton de session

Supprimer le compte

Requis pour la revue App Store (les applications avec création de compte doivent proposer la suppression dans l'app). Anonymise le compte (e-mail brouillé et libéré pour une nouvelle inscription, nom/avatar/mot de passe effacés) plutôt que de supprimer définitivement les lignes, afin que les commandes passées restent sur les registres pour la comptabilité/fiscalité. Détruit toutes les sessions et tous les appareils push enregistrés de l'utilisateur.

Renvoie 409 si walletBalance > 0 — l'application doit indiquer à l'utilisateur de retirer les fonds ou de contacter le support d'abord, plutôt que de les faire perdre silencieusement.

Catalogue

GET/api/v1/destinationsAucune authentification

Lister les destinations

Pays et régions avec forfaits actifs, tarifés et formatés pour l'UI (c'est ce que la boutique elle-même appelle). Optionnel ?filter=popular|regional|global|<texte de recherche>.

GET/api/v1/destinations/:codeAucune authentification

Détail d'une destination

Détail complet (description, forfaits) pour un pays par code ISO, ex. /api/v1/destinations/jp.

Achats

POST/api/v1/checkoutJeton de session

Paiement

Achète un forfait directement (par opposition à un dépôt préalable dans le portefeuille).

FieldNotes
planId*
quantityfacultatif · 1–10, défaut 1
paymentMethod*wallet | stripe | crypto
successUrl, cancelUrlfacultatif · pour les redirections stripe/crypto. Utilisez un lien universel/app, pas une simple URL web, afin que la redirection rouvre l'application.
200 OK — wallet (synchronous)
{ "success": true, "orderId": "..." }
200 OK — stripe / crypto (redirect required)
{ "success": true, "orderId": "...", "url": "https://checkout.stripe.com/..." }
Ouvrez url dans un navigateur intégré (SFSafariViewController / Chrome Custom Tabs) plutôt qu'une simple WebView — Stripe et Cryptomus attendent tous deux un vrai contexte de navigateur.
POST/api/v1/payments/stripe/create-sessionJeton de session

Rechargement du portefeuille via Stripe

Recharge le portefeuille par carte — un flux distinct du paiement. Corps : { amount (centimes, min 200), currency, successUrl, cancelUrl }. Renvoie { sessionId, url }.

POST/api/v1/payments/cryptomus/create-invoiceJeton de session

Rechargement du portefeuille en crypto

Corps : { amount (chaîne décimale, min $2), currency, url_return }. Renvoie l'objet facture Cryptomus, y compris url.

GET/api/v1/user/ordersJeton de session

Lister les commandes

Toutes les commandes de l'appelant, les plus récentes en premier.

GET/api/v1/user/orders/:idJeton de session

Détail d'une commande

Une commande, incluant esimIds — récupérez le détail complet de l'eSIM (QR, code d'activation) via GET /api/v1/user/esims et faites correspondre par id.

GET/api/v1/user/esimsJeton de session

Mes eSIM

Chaque eSIM possédée par l'appelant : iccid, activationCode, qrCodeUrl, smdpAddress, status, dataUsage/totalVolume (Mo), activatedAt, expiresAt. C'est ce qui alimente l'écran "Mes eSIM" et l'affichage du QR.

GET/api/v1/user/wallet/transactionsJeton de session

Historique du portefeuille

Historique des dépôts/retraits/achats/remboursements pour le solde du portefeuille affiché dans /auth/me.

Support

GET/api/v1/support/ticketsJeton de session

Lister les tickets

Les tickets de l'appelant avec leurs fils de messages complets, les plus récents en premier.

POST/api/v1/support/ticketsJeton de session

Nouveau ticket

Corps : { subject, category, message } (category facultatif).

POST/api/v1/support/tickets/:id/messagesJeton de session

Répondre à un ticket

Corps : { message }. Répondre à un ticket RESOLVED/CLOSED le rouvre automatiquement.

Appareils pour notifications push

Enregistre uniquement les jetons d'appareil — l'envoi effectif nécessite des identifiants APNs/FCM, voir l'encadré ci-dessous.

POST/api/v1/user/push-devicesJeton de session

Enregistrer un appareil

À appeler après obtention d'un jeton APNs ou FCM. Corps : { platform: "IOS"|"ANDROID", pushToken, appVersion }. Fait un upsert par jeton, donc rappeler à chaque lancement de l'app est sûr et recommandé (les jetons peuvent changer).

DELETE/api/v1/user/push-devicesJeton de session

Désenregistrer un appareil

Corps : { pushToken }. À appeler lors de la déconnexion afin qu'un appareil partagé/réinitialisé cesse de recevoir les notifications d'un autre utilisateur.

API Partenaire

Tout ce qui suit nécessite l'en-tête de clé API décrit ci-dessus — aucun cookie de session ne s'applique ici.

Catalogue

GET/api/public/v1/destinationsClé API

Lister les destinations

Catalogue complet, sous une forme orientée partenaire (pas la forme UI de la boutique).

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/:codeClé API

Détail d'une destination

Un pays par code ISO (ex. /api/public/v1/destinations/JP), même forme de forfaits que ci-dessus.

Commandes

POST/api/public/v1/ordersClé API

Créer une commande

Achète un forfait sur le solde du portefeuille de l'appelant et provisionne l'eSIM de manière synchrone — la réponse contient déjà les données QR/activation, prêtes à remettre à votre client final. Aucun sondage nécessaire dans le cas nominal.

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" }
  ]
}
Modes d'échec : 402 INSUFFICIENT_BALANCE (rechargez d'abord), 404 PLAN_NOT_FOUND, ou 502 FULFILLMENT_FAILED (débité, mais le fournisseur n'a pas pu émettre l'eSIM — l'id de commande est inclus, le support est notifié automatiquement ; sondez GET /orders/:id plutôt que de redébiter).
GET/api/public/v1/ordersClé API

Lister les commandes

Optionnel ?limit= (défaut 25, max 100). Forme résumée — utilisez le point de terminaison de détail pour les données eSIM.

GET/api/public/v1/orders/:idClé API

Détail d'une commande

Détail complet de la commande incluant les données QR/activation et l'usage en direct (dataUsageMb / totalVolumeMb) de chaque eSIM — sondez ceci pour montrer à votre client ses données restantes.

Portefeuille

GET/api/public/v1/walletClé API

Solde du portefeuille

Sondez avant de passer un gros lot de commandes, ou alertez-vous quand le solde est bas.

200 OK
{ "balanceUsd": 128.40, "currency": "USD" }

Ce qui n'est pas encore construit

Pour que rien ici ne soit présumé fonctionnel alors qu'il ne l'est pas.

Envoi des notifications push

L'enregistrement du jeton fonctionne ; l'envoi effectif nécessite des identifiants APNs/FCM.

Tarification spécifique au partenaire

L'API Partenaire facture actuellement le même prix de détail que la boutique. Une tarification de gros/négociée nécessiterait un changement de schéma et une décision commerciale.

Jetons de rafraîchissement

Les sessions sont des jetons plats à longue durée de vie (30 jours), pas des paires jeton d'accès court + jeton de rafraîchissement. Suffisant pour l'instant ; à revoir plus tard pour une posture de sécurité plus stricte.

Écran de paiement Stripe natif

L'intégration Stripe utilise le Checkout hébergé (redirection/webview). Une saisie de carte native utiliserait plutôt PaymentIntents + le SDK Stripe.

Webhooks pour les partenaires

Les partenaires doivent sonder GET /orders/:id pour le statut ; il n'y a pas encore de webhook sortant (ex. "eSIM activée").

Référence API Vio eSIM et documentation Partner API