Vio eSIM
REST API · v1

Vio eSIM API 레퍼런스

동일한 백엔드 위의 두 가지 REST 인터페이스: 웹 앱과 iOS/Android 앱을 구동하는 고객용 API, 그리고 Vio eSIM을 프로그래밍 방식으로 재판매하기 위한 별도의 API 키 인증 파트너 API입니다.

모바일 / 웹 API
https://vioesim.com/api/v1
파트너 API
https://vioesim.com/api/public/v1

모바일 앱 인증

웹 앱과 네이티브 앱은 동일한 세션 시스템을 공유합니다 — 네이티브 앱은 쿠키 저장소에 의존하는 대신 토큰 자체를 보관합니다.

가입 또는 로그인. 두 엔드포인트 모두 사용자 객체와 함께 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상태 충돌(예: 잔액이 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자리 코드, 예: ko
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 코드로 단일 국가의 전체 상세 정보(설명, 요금제)를 조회합니다. 예: /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 키 헤더가 필요합니다 — 여기서는 세션 쿠키가 적용되지 않습니다.

카탈로그

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 키

주문 상세 정보

각 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 문서