Vio eSIM
REST API · v1

Vio eSIM API リファレンス

同じバックエンド上の2つのREST表面:ウェブアプリとiOS/Androidアプリを支える顧客向けAPIと、Vio eSIMをプログラムで再販するための、APIキーで認証される別個のパートナーAPI。

モバイル / ウェブ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

カタログを閲覧し、2回の呼び出しで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競合状態(例:残高が0でないアカウントの削除)
429レート制限に到達 — Retry-Afterヘッダー(秒)を参照
502決済は完了したがeSIMのプロビジョニングに失敗 — 決済を再試行しないでください

レート制限(パートナーAPI): 読み取りはキーごとに120リクエスト/分、POST /orders は30リクエスト/分。

モバイル / ウェブ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 }。?token= を付けたGETでフォーム表示前に有効性を確認できます({ "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文字コード、例:ja
200 OK
{ "success": true, "user": { ...same shape as /auth/me } }
POST/api/v1/user/avatarセッショントークン

アバターの更新

ボディ:{ "avatarUrl": "data:image/..." } — base64のdata URI、最大約1.5MB。

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審査のために必須(アカウント作成機能があるアプリはアプリ内削除を提供する必要があります)。過去の注文が税務・会計記録として残るよう、行を完全に削除するのではなくアカウントを匿名化します(メールは撹拌されて再登録可能になり、名前/アバター/パスワードは消去されます)。ユーザーの全セッションと登録済みプッシュデバイスを破棄します。

walletBalance が0より大きい場合は409を返します — アプリは資金を黙って失わせるのではなく、先に出金するかサポートに連絡するようユーザーに案内する必要があります。

カタログ

GET/api/v1/destinations認証不要

渡航先一覧の取得

有効なプランを持つ国と地域を、価格付き・UI用にフォーマットして返します(ストアフロント自体が呼び出しているものと同じです)。任意 ?filter=popular|regional|global|<検索テキスト>。

GET/api/v1/destinations/:code認証不要

渡航先の詳細

ISOコードによる1つの国の完全な詳細(説明、プラン)、例:/api/v1/destinations/jp。

購入

POST/api/v1/checkoutセッショントークン

チェックアウト

先にウォレットへ入金するのではなく、直接プランを購入します。

FieldNotes
planId*
quantity任意 · 1〜10、デフォルト1
paymentMethod*wallet | stripe | crypto
successUrl, cancelUrl任意 · stripe/cryptoのリダイレクト用。リダイレクトでアプリが再度開くよう、単なるウェブURLではなくユニバーサル/アプリリンクを使用してください。
200 OK — wallet (synchronous)
{ "success": true, "orderId": "..." }
200 OK — stripe / crypto (redirect required)
{ "success": true, "orderId": "...", "url": "https://checkout.stripe.com/..." }
url は単なるWebViewではなく、アプリ内ブラウザ(SFSafariViewController / Chrome Custom Tabs)で開いてください — 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 }。url を含むCryptomusのインボイスオブジェクトを返します。

GET/api/v1/user/ordersセッショントークン

注文一覧

呼び出し元の全注文を新しい順に返します。

GET/api/v1/user/orders/:idセッショントークン

注文の詳細

esimIds を含む単一の注文 — GET /api/v1/user/esims で完全なeSIM詳細(QR、アクティベーションコード)を取得し、idで照合してください。

GET/api/v1/user/esimsセッショントークン

マイeSIM

呼び出し元が所有する各eSIM:iccid、activationCode、qrCodeUrl、smdpAddress、status、dataUsage/totalVolume(MB)、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のチケットに返信すると自動的に再オープンされます。

プッシュ通知デバイス

デバイストークンの登録のみ行います — 実際の送信には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コードによる1つの国(例:/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キー

注文の詳細

各eSIMのQR/アクティベーションデータとリアルタイム使用量(dataUsageMb / totalVolumeMb)を含む完全な注文詳細 — 残りデータ量を顧客に表示するためこれをポーリングしてください。

ウォレット

GET/api/public/v1/walletAPIキー

ウォレット残高

大量の注文をまとめて行う前に確認するか、残高が少なくなったときに自身に通知してください。

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

まだ構築されていないもの

ここに書かれていないものが動作すると誤解されないように。

プッシュ通知の送信

トークン登録は動作しますが、実際の送信にはAPNs/FCMの認証情報が必要です。

パートナー別の価格設定

パートナーAPIは現在ストアフロントと同じ小売価格を課金します。卸売/交渉価格にはスキーマ変更とビジネス上の意思決定が必要です。

リフレッシュトークン

セッションは、短命なアクセストークンとリフレッシュトークンのペアではなく、長命(30日)なフラットトークンです。当面は十分ですが、より厳格なセキュリティ体制が必要になれば後日見直します。

ネイティブStripe決済画面

Stripe連携はホスト型Checkout(リダイレクト/ウェブビュー)を使用しています。ネイティブのカード入力UIにはPaymentIntents+Stripe SDKを使用することになります。

パートナー向けWebhook

パートナーはステータス確認のため GET /orders/:id をポーリングする必要があります。まだ発信Webhook(例:「eSIMがアクティベートされました」)はありません。

Vio eSIM APIリファレンス&パートナーAPIドキュメント