Vio eSIM
REST API · v1

Справочник API Vio eSIM

Два REST-интерфейса на одном бэкенде: клиентский API, на котором работает веб-приложение и наши приложения для iOS/Android, и отдельный Партнёрский API с аутентификацией по API-ключу для программной перепродажи Vio eSIM.

Мобильный / веб API
https://vioesim.com/api/v1
Партнёрский API
https://vioesim.com/api/public/v1

Аутентификация мобильного приложения

Веб-приложение и нативные приложения используют одну систему сессий — нативное приложение просто хранит сам токен вместо использования cookie.

Регистрация или вход. Оба эндпоинта возвращают поле token вместе с объектом пользователя (а также заголовок Set-Cookie, который вместо этого использует веб-приложение). Храните token в Keychain (iOS) или в хранилище на основе Keystore (Android).

Отправляйте его обратно в каждом запросе:

Authorization: Bearer <token>

Срок действия токена: 30 дней с момента выдачи или до вызова /api/v1/auth/logout. Шага обновления токена нет — токен с истекающим сроком просто заменяется повторным входом пользователя.

Сессия для заблокированной, забаненной или удалённой учётной записи перестаёт работать немедленно — проверяется на сервере при каждом запросе, даже если срок действия самого токена ещё не истёк.

Аутентификация Партнёрского API

Для внешних компаний, перепродающих Vio eSIM через собственный сайт или приложение. Партнёр — это обычная учётная запись Vio eSIM: создайте ключ в разделе Панель управления → API-ключи (показывается один раз — сохраните его, повторно он показан не будет), затем отправляйте его в каждом запросе:

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

Создание нового ключа немедленно делает предыдущий недействительным.

Модель оплаты: Заказы через Партнёрский API оплачиваются синхронно с предоплаченного баланса кошелька аккаунта — без перенаправления на оплату, так как это серверный запрос. Пополните баланс через панель управления (картой/криптовалютой) перед размещением заказов; POST /orders возвращает 402, если баланса недостаточно.

Быстрый старт — Партнёрский API

Просмотрите каталог и купите eSIM за два вызова:

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

Ошибки и ограничения частоты запросов

Мобильный/веб API возвращает { "error": "сообщение" } (сообщения сейчас на турецком). Партнёрский API возвращает структурированный формат, чтобы вы могли обрабатывать по code:

{ "error": { "code": "INSUFFICIENT_BALANCE", "message": "Insufficient wallet balance." } }
СтатусЗначение
401Отсутствующий/неверный/истёкший токен или API-ключ
402Только Партнёрский API — недостаточно средств на балансе
404Ресурс не найден или не принадлежит вызывающей стороне
409Конфликтующее состояние (напр. удаление аккаунта с ненулевым балансом)
429Достигнут лимит частоты запросов — см. заголовок Retry-After (в секундах)
502Платёж списан, но выпуск eSIM не удался — не повторяйте списание

Ограничения частоты (Партнёрский API): 120 запросов/мин на ключ для чтения, 30 запросов/мин на POST /orders.

Мобильный / веб API

Аутентификация

POST/api/v1/auth/registerБез аутентификации

Регистрация

Создаёт учётную запись и активную сессию.

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/loginБез аутентификации

Вход

Тело: { email, password }. Тот же формат ответа, что и у регистрации. 401 при неверных данных, 403 если аккаунт заблокирован.

POST/api/v1/auth/logoutТокен сессии

Выход

Без тела запроса. Удаляет сессию на сервере — вызывайте при реальном выходе, а не просто «забыв токен локально», чтобы украденный токен не мог продолжать работать.

GET/api/v1/auth/meТокен сессии · необязательно

Текущий пользователь

Возвращает { "user": null } (никогда не ошибку) при выходе из системы — используйте при запуске приложения, чтобы решить, показывать ли экран входа.

POST/api/v1/auth/forgot-passwordБез аутентификации

Забыли пароль

Тело: { email, locale }. Всегда возвращает { success: true } независимо от существования адреса, чтобы нельзя было перебирать аккаунты. Отправляет письмо со ссылкой/токеном сброса (действует 1 час).

POST/api/v1/auth/reset-passwordБез аутентификации

Сброс пароля

Тело: { token, newPassword }. GET с ?token= проверяет действительность перед показом формы ({ "valid": true|false }).

POST/api/v1/auth/change-passwordТокен сессии

Смена пароля

Тело: { currentPassword, newPassword }. currentPassword обязателен, если у аккаунта ещё нет пароля (напр. вход через соцсеть).

Аккаунт

PATCH/api/v1/user/profileТокен сессии

Обновить профиль

Отправляйте только те поля, которые хотите изменить.

FieldNotes
firstName, lastNameнеобязательно · строка
preferredCurrencyнеобязательно · одно из USD EUR GBP TRY
localeнеобязательно · 2-буквенный код, напр. ru
200 OK
{ "success": true, "user": { ...same shape as /auth/me } }
POST/api/v1/user/avatarТокен сессии

Обновить аватар

Тело: { "avatarUrl": "data:image/..." } — base64 data URI, макс. ~1,5 МБ.

POST/api/v1/user/api-key/generateТокен сессии

Создать API-ключ

Выдаёт новый Партнёрский API-ключ для этого аккаунта, заменяя предыдущий. Сырой ключ показывается только один раз, в этом ответе — впоследствии доступен только его префикс.

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/accountТокен сессии

Удалить аккаунт

Требуется для проверки App Store (приложения с созданием аккаунта должны предлагать удаление внутри приложения). Обезличивает аккаунт (email заменяется и освобождается для повторной регистрации, имя/аватар/пароль удаляются) вместо окончательного удаления строк, чтобы прошлые заказы оставались в бухгалтерских/налоговых записях. Удаляет все сессии и зарегистрированные push-устройства пользователя.

Возвращает 409, если walletBalance > 0 — приложение должно сначала предложить пользователю вывести средства или обратиться в поддержку, а не молча их списывать.

Каталог

GET/api/v1/destinationsБез аутентификации

Список направлений

Страны и регионы с активными тарифами, с ценами и в формате для UI (это то, что вызывает сама витрина). Необязательно ?filter=popular|regional|global|<текст поиска>.

GET/api/v1/destinations/:codeБез аутентификации

Детали направления

Полная информация (описание, тарифы) для одной страны по коду ISO, напр. /api/v1/destinations/jp.

Покупки

POST/api/v1/checkoutТокен сессии

Оформление заказа

Покупает тариф напрямую (в отличие от предварительного пополнения кошелька).

FieldNotes
planId*
quantityнеобязательно · 1–10, по умолчанию 1
paymentMethod*wallet | stripe | crypto
successUrl, cancelUrlнеобязательно · для перенаправлений stripe/crypto. Используйте универсальную/app-ссылку, а не обычный веб-URL, чтобы перенаправление снова открывало приложение.
200 OK — wallet (synchronous)
{ "success": true, "orderId": "..." }
200 OK — stripe / crypto (redirect required)
{ "success": true, "orderId": "...", "url": "https://checkout.stripe.com/..." }
Открывайте url во встроенном браузере (SFSafariViewController / Chrome Custom Tabs), а не в обычном WebView — и Stripe, и Cryptomus ожидают настоящий контекст браузера.
POST/api/v1/payments/stripe/create-sessionТокен сессии

Пополнение кошелька через Stripe

Пополняет кошелёк картой — отдельный от оформления заказа процесс. Тело: { amount (в центах, мин. 200), currency, successUrl, cancelUrl }. Возвращает { sessionId, url }.

POST/api/v1/payments/cryptomus/create-invoiceТокен сессии

Пополнение кошелька криптовалютой

Тело: { amount (строка с десятичным числом, мин. $2), currency, url_return }. Возвращает объект счёта Cryptomus, включая url.

GET/api/v1/user/ordersТокен сессии

Список заказов

Все заказы вызывающей стороны, сначала новые.

GET/api/v1/user/orders/:idТокен сессии

Детали заказа

Один заказ, включая esimIds — получите полные данные eSIM (QR, код активации) через GET /api/v1/user/esims и сопоставьте по id.

GET/api/v1/user/esimsТокен сессии

Мои eSIM

Каждая eSIM, принадлежащая вызывающей стороне: iccid, activationCode, qrCodeUrl, smdpAddress, status, dataUsage/totalVolume (МБ), activatedAt, expiresAt. Это данные для экрана «Мои eSIM» и отображения QR.

GET/api/v1/user/wallet/transactionsТокен сессии

История кошелька

История пополнений/выводов/покупок/возвратов для баланса кошелька, показанного в /auth/me.

Поддержка

GET/api/v1/support/ticketsТокен сессии

Список обращений

Обращения вызывающей стороны с полными цепочками сообщений, сначала новые.

POST/api/v1/support/ticketsТокен сессии

Новое обращение

Тело: { subject, category, message } (category необязательно).

POST/api/v1/support/tickets/:id/messagesТокен сессии

Ответить на обращение

Тело: { message }. Ответ на обращение со статусом RESOLVED/CLOSED автоматически переоткрывает его.

Устройства push-уведомлений

Регистрирует только токены устройств — для реальной отправки нужны учётные данные APNs/FCM, см. примечание ниже.

POST/api/v1/user/push-devicesТокен сессии

Регистрация устройства

Вызывайте после получения токена APNs или FCM. Тело: { platform: "IOS"|"ANDROID", pushToken, appVersion }. Выполняет upsert по токену, поэтому повторный вызов при каждом запуске приложения безопасен и рекомендуется (токены могут меняться).

DELETE/api/v1/user/push-devicesТокен сессии

Отмена регистрации устройства

Тело: { pushToken }. Вызывайте при выходе из системы, чтобы общее/сброшенное устройство перестало получать уведомления другого пользователя.

Партнёрский API

Для всего перечисленного ниже требуется заголовок с API-ключом, описанный выше — cookie сессии здесь не применяется.

Каталог

GET/api/public/v1/destinationsAPI-ключ

Список направлений

Полный каталог в формате, ориентированном на партнёров (не в UI-формате витрины).

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/:codeAPI-ключ

Детали направления

Одна страна по коду ISO (напр. /api/public/v1/destinations/JP), тот же формат тарифов, что и выше.

Заказы

POST/api/public/v1/ordersAPI-ключ

Создать заказ

Покупает тариф с баланса кошелька вызывающей стороны и синхронно выпускает eSIM — ответ уже содержит данные QR/активации, готовые к передаче конечному клиенту. В штатном случае опрос не требуется.

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" }
  ]
}
Сценарии сбоя: 402 INSUFFICIENT_BALANCE (сначала пополните баланс), 404 PLAN_NOT_FOUND, или 502 FULFILLMENT_FAILED (списано, но провайдер не смог выпустить eSIM — id заказа включён, поддержка уведомляется автоматически; опрашивайте GET /orders/:id вместо повторного списания).
GET/api/public/v1/ordersAPI-ключ

Список заказов

Необязательно ?limit= (по умолчанию 25, макс. 100). Сокращённый формат — используйте эндпоинт деталей для данных eSIM.

GET/api/public/v1/orders/:idAPI-ключ

Детали заказа

Полные детали заказа, включая данные QR/активации и текущее использование (dataUsageMb / totalVolumeMb) каждой eSIM — опрашивайте это, чтобы показать клиенту оставшийся трафик.

Кошелёк

GET/api/public/v1/walletAPI-ключ

Баланс кошелька

Проверяйте перед размещением большой партии заказов или настройте оповещение при низком балансе.

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

Что ещё не реализовано

Чтобы ничего здесь не считалось работающим, если это не так.

Отправка push-уведомлений

Регистрация токенов работает; для реальной отправки нужны учётные данные APNs/FCM.

Индивидуальные цены для партнёров

Партнёрский API сейчас взимает ту же розничную цену, что и витрина. Оптовые/договорные цены потребуют изменения схемы и бизнес-решения.

Refresh-токены

Сессии — это долгоживущие (30 дней) простые токены, а не пары короткоживущий access + refresh токен. Пока достаточно; пересмотреть позже для более строгой модели безопасности.

Нативный экран оплаты Stripe

Интеграция Stripe использует размещённый Checkout (перенаправление/webview). Нативный ввод карты вместо этого использовал бы PaymentIntents + SDK Stripe.

Вебхуки для партнёров

Партнёры должны опрашивать GET /orders/:id для получения статуса; исходящего вебхука (напр. «eSIM активирована») пока нет.

Справочник API Vio eSIM и документация Partner API