Tổng Kho eSIM
OpenAPI 2.4.0 · 작업 30개

API 레퍼런스

백엔드 생성 명세에서 모든 /v1 경로, 매개변수, 스키마, 열거형, 오류 코드를 직접 표시합니다.

운영

https://api.tongkhoesim.com/v1

테스트 모드

https://api-testing.tongkhoesim.com/v1

실제 환경: api.tongkhoesim.com / tke_live_(파트너) 또는 tke_member_live_(허가된 회원). 테스트: api-testing.tongkhoesim.com / tke_test_ / tke_member_test_(파트너 / 회원).

공통 규칙

다음 6개 규칙은 모든 30개 작업에 적용됩니다.

인증

GET을 포함한 모든 요청에 HMAC-SHA256 및 헤더 4개(X-TKE-Key · X-TKE-Timestamp · X-TKE-Nonce · X-TKE-Signature)가 필요합니다.

응답 형식
성공: { ok: true, data }. 오류: { ok: false, error: { code, message, field?, meta?, request_id } }. 적절한 HTTP 상태를 사용하며 오류에 200을 반환하지 않습니다.
데이터 유형
금액은 정수 VND(BIGINT), 데이터는 카탈로그를 포함해 바이트(data.total_bytes / data.daily_bytes)입니다. 시간은 ISO-8601 UTC이며 표시 시 현지 시간으로 변환하세요.
호출 빈도 제한
429 응답의 Retry-After 및 X-RateLimit-Limit / Remaining / Reset을 확인하세요. 한도는 키별로 설정됩니다.
request_id
모든 응답의 X-Request-Id와 오류의 error.request_id를 기록하세요. 기술 지원 시 요청 추적에 사용합니다.
Idempotency-Key

24시간 내 동일 키와 본문은 재차 차감 없이 기존 결과를 반환합니다. api_key_id별로 적용하며 5개 작업(POST /v1/orders, POST /v1/orders/bulk, POST /v1/esims/{id}/cancel, POST /v1/esims/{id}/change-plan, POST /v1/esims/{id}/refund-requests)에 필수입니다.

두 가지 페이지 방식

응답 형식과 종료 조건이 다르므로 각각에 맞게 처리하세요.

오프셋 · page / page_size

GET /v1/webhooks/deliveries에서만 사용하며 page, page_size, total을 반환합니다.

범위를 넘으면 HTTP 200과 items: [], 실제 total을 반환합니다. 목록이 비거나 total만큼 처리하면 종료하세요.

커서 · cursor / next_cursor

/v1/catalog/packages, /v1/orders, /v1/esims, /v1/esims/{id}/plan-options에서 사용합니다. updated_since는 orders와 esims만 지원합니다.

next_cursor가 없거나 현재 커서와 같으면 종료하세요. data에서 필드가 생략될 수 있습니다.

잘못된 커서와 HTTP 200의 함정

잘못된 커서는 기존 클라이언트 호환성을 위해 처음으로 돌아갑니다. next_cursor !== null만 검사하면 첫 페이지가 무한 반복됩니다.

javascript
let cursor = null;
const seen = new Set();

while (true) {
  const params = new URLSearchParams({ page_size: "200" });
  if (cursor) params.set("cursor", cursor);

  // callSigned는 X-TKE-* 헤더 4개를 추가하는 함수입니다(HMAC 참조).
  const page = await callSigned("GET", `/v1/orders?${params}`);
  for (const order of page.data.items) await handle(order);

  const next = page.data.next_cursor;
  if (!next || next === cursor || seen.has(next)) break;
  seen.add(next);
  cursor = next;
}

카탈로그

두 공급 시스템을 통합한 하나의 요금제 카탈로그입니다. 시스템은 별도 경로가 아닌 `market` 필드(`domestic` | `international`)와 `?market=` 필터로 구분합니다. 수량이나 `available`/`low`/`out_of_stock` 같은 재고 개념은 없습니다. 요금제를 표시하고 공급업체가 제공할 수 없는 주문은 `POST /v1/orders`에서 `esim.out_of_stock` 등의 명확한 오류를 반환합니다. 통일된 커서 페이지 처리를 사용합니다. `total`은 집계 비용이 적을 때(국내 시스템 필터)에만 제공하며 그 외에는 `null`입니다.

GET/v1/catalog/packages

요금제 목록 조회

scope catalog:readget_catalog_packages

필요한 권한 범위: catalog:read.

조직의 요금제 카탈로그로, 두 공급 시스템을 하나의 화면에서 제공하고 ?market=으로 필터링합니다.

실제로 구매 가능한 요금제만 반환합니다. 게시 상태이고 공급업체가 계속 판매하며 키의 제한 범위 안에 있고 조직 가격까지 설정되어야 합니다. 미정가 요금제는 주문할 수 없으므로 표시하지 않습니다.

커서 페이지 처리: 처음에는 cursor 없이 호출하고 이후 next_cursor를 그대로 보내 null이 될 때까지 반복합니다. page_size보다 짧은 페이지도 끝을 의미하지 않습니다.

capabilities는 실제 요금제 선언만 따르며 추측하지 않습니다. 미명시 요금제는 모든 플래그가 false입니다. 반드시 422를 반환할 기능을 약속하는 것보다 가능한 버튼을 생략하는 편을 선택합니다.

카탈로그에는 수량이나 재고 있음/없음 등 재고 개념이 없습니다. 공급업체가 주문을 이행할 수 없으면 POST /v1/orders에서 esim.out_of_stock 등의 명확한 오류를 반환하므로 사전 표시 필드로 추측할 필요가 없습니다.

쿼리 매개변수 (18)

page_sizeinteger선택기본값 50min 1max 100

페이지당 레코드 수입니다. 기본 50, 최대 100이며 더 큰 값을 보내면 오류 없이 100으로 제한됩니다.

marketstring선택

공급 시스템은 domestic | international입니다. 목록에 없는 값은 전체 카탈로그가 아닌 internal.invalid_filter(400)를 반환하여 오타가 필터 없음으로 오해되지 않게 합니다.

countrystring선택

ISO-3166 alpha-2 국가 코드입니다(예: VN). 해당 국가를 포함하는 패키지만 반환합니다.

regionstring선택

지역 슬러그입니다(예: chau-a). 해당 지역의 국가를 하나 이상 지원하는 요금제와 모든 글로벌 요금제를 포함합니다.

qstring선택

공개 코드와 요금제 이름을 부분 검색합니다(대소문자 구분 없음).

tagstring선택

하나의 상품 태그로 필터링합니다. 저장 시와 동일하게 공백을 정리하고 소문자와 하이픈으로 정규화하므로 Ban Le와 ban-le는 같은 태그입니다.

data_modelstring선택

데이터 유형: daily | fixed | unlimited. 기존 supports_topup을 대체합니다. 알 수 없는 값은 internal.invalid_filter(400)입니다.

data_min_bytesinteger · int64선택

최소 데이터 용량(바이트)입니다. 일일 요금제는 일일 용량으로 비교하며, 무제한 요금제는 하한 조건을 항상 충족합니다.

data_max_bytesinteger · int64선택

최대 데이터 용량(바이트)입니다. 무제한 요금제는 제외됩니다. 무제한에 상한을 적용할 수 없으며, 포함하면 작은 요금제를 찾는 사용자에게 무제한 요금제가 제시됩니다.

days_mininteger · int64선택

최소 유효기간(일)입니다.

days_maxinteger · int64선택

최대 유효기간(일)입니다.

price_mininteger · int64선택

소속 조직의 가격과 비교하는 최소 가격(VND)입니다. 서버에서 페이지를 나눈 후 적용되므로 결과가 적더라도 마지막 페이지라는 뜻은 아닙니다.

price_maxinteger · int64선택

최대 가격(VND)이며 price_min과 같은 규칙을 따릅니다.

network_genstring선택

해당 네트워크 세대(2g | 3g | 4g | 5g)에서 사용할 수 있는 요금제만 조회합니다. 알 수 없는 값은 internal.invalid_filter(400)를 반환합니다.

hotspotstring선택
허용 값:truefalse

true는 Wi-Fi 공유 지원 명시, false는 미지원 명시 패키지입니다. 미선언(features.hotspot = null)은 어느 쪽에도 해당하지 않습니다. 문자열 true/false만 허용합니다.

appstring선택

최소 하나의 운영체제에서 해당 앱을 지원한다고 명시한 요금제만 조회합니다(app=tiktok). 미명시 요금제는 일치하지 않으며, 정보가 없다고 지원을 보장하는 것은 아닙니다. 현재 tiktok만 허용하고 그 외 코드는 internal.invalid_filter(400)를 반환합니다.

voicestring선택
허용 값:truefalse

true는 음성 지원 명시, false는 미지원 명시 패키지입니다. 미선언은 어느 쪽에도 해당하지 않습니다. 문자열 true/false만 허용합니다.

cursorstring선택

이전 next_cursor를 그대로 보내세요. 페이지가 짧아도 계속하고 null일 때만 종료하세요. 키 제한, 조직 가격, 가격 필터는 페이지를 나눈 후 적용됩니다. 커서에는 필터 지문이 있어 다른 필터나 손상된 커서를 보내면 catalog.cursor_mismatch(400)를 반환해 서로 다른 조회 결과의 혼합을 막습니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
itemsobject[]
—
capabilitiesobject

공급업체 유형으로 추론하지 않고 패키지가 실제 선언한 판매 후 기능입니다. 미선언 패키지는 모든 값이 false이며 반드시 422 capability.not_supported가 될 작업을 약속하지 않습니다.

cancelobject
—
restrictionstring
null 허용

code_not_disclosed는 고객 전달, 대시보드 열람, ?include=lpa 등으로 아무도 코드를 보지 않았을 때만 취소할 수 있다는 뜻입니다. 실제 취소 조건이며 시스템이 알 수 없는 설치 여부를 의미하지 않습니다. 이전 unused_only 표기는 부정확했습니다.

code_not_disclosednull
supportedboolean
—
plan_changeobject
—
policyobject
null 허용

이 공급업체에서 새 요금제를 추가 충전하는 방식입니다. null은 충전할 수 없거나 공급업체 정책을 아직 확인하지 못했다는 뜻입니다. 추측해서는 안 됩니다. 모든 값이 false인 객체가 아무것도 취소되지 않음을 보장하는 것과는 의미가 다릅니다.

……

더 깊은 필드는 JSON 예시를 확인하세요.

supportedboolean
—
usage_checkobject
—
supportedboolean
—
codestring

자사 카탈로그의 요금제 코드인 public_sku입니다. 공급업체가 변경되어도 요금제별로 불변이므로 대사 키로 안전하게 사용할 수 있습니다. 예시는 형식만 맞고 실제로 작동하지 않으니 GET /v1/catalog/packages에서 실제 코드를 조회하세요.

coverageobject

패키지 사용 가능 지역입니다.

countriesstring[]

ISO-3166 alpha-2 코드입니다. 글로벌 요금제(200개국을 모두 나열하지 않음)와 지원 국가가 명시되지 않은 요금제는 빈 배열입니다.

country_countinteger

모든 데이터를 조회하고 배열을 직접 세지 않고도 다국가 요금제를 정렬하거나 필터링할 수 있도록 제공하는 지원 국가 수입니다.

groupstring

범위: 국가 목록(country), 지역(region), 전 세계(global). 열린 열거형이므로 알 수 없는 값을 무시해야 합니다.

regionstring
null 허용

항상 null입니다. 지역은 패키지가 아닌 국가에 연결되므로 값을 추론하지 않습니다. ?region= 필터는 계속 작동합니다.

dataobject

요금제 데이터 용량입니다. 이중 반올림을 피하도록 모든 수치는 MB/GB가 아닌 바이트 단위입니다.

daily_bytesinteger · int64
null 허용

바이트 단위의 일일 데이터 용량입니다. fixed/unlimited 요금제는 null입니다.

labelstring

모든 화면에서 사용하는 동일한 함수로 생성한 표시 이름입니다.

modelstring

용량 계산 방식: daily(일일 용량), fixed(전체 기간 총량), unlimited입니다. 확장 가능한 열거형이므로 클라이언트는 알 수 없는 값을 무시해야 합니다.

dailyfixedunlimited
resetobject
null 허용

일일 데이터 초기화 시점입니다. 늦은 시간 구매 시 첫날 이용량에 영향을 주는 판매 정보입니다. null은 미선언입니다.

atstring
null 허용

timezone 기준 현지 초기화 시각입니다(00:00, 00:00:01).

notestring
null 허용
—
timezonestring
null 허용

초기화 시점의 시간대입니다(UTC+8, UTC+7).

total_bytesinteger · int64
null 허용

전체 기간 데이터 용량(바이트)입니다. daily/unlimited는 null입니다. 일일 용량에 일수를 곱한 값은 공급업체가 보장한 총량이 아닙니다.

descriptionstring
null 허용

패키지 판매 설명입니다. 작성되지 않았으면 null입니다.

description_enstring
null 허용

영문 설명입니다. 제공되지 않으면 null입니다.

featuresobject

패키지 기능을 코드 배열 대신 타입이 있는 객체로 표시합니다. []로는 핫스팟 미지원과 미확인을 구분할 수 없지만 false와 null은 구분할 수 있습니다.

appsobject[]

운영체제별 앱 지원 상태입니다.

배열에 없는 앱은 정보가 없다는 뜻이며, 지원한다는 뜻이 아닙니다. 목록에 있는 항목만 공급업체가 명시한 정보이고 현재는 TikTok만 명시되어 있습니다.

통합 필드는 의도적으로 제공하지 않습니다. 실제 카탈로그에서 2,465개 요금제가 iPhone의 TikTok을 지원하지만 Android에서는 지원하지 않습니다. 하나의 통합 플래그로 모두 미지원 처리하면 카탈로그의 약 3분의 1을 잘못 안내하게 됩니다.

androidstring

supported(공급업체 지원 명시), blocked(미지원 명시), unknown(해당 OS 정보 없음). 열린 열거형입니다.

supportedblockedunknown
appstring

소문자 앱 코드입니다(예: tiktok).

iosstring

supported(공급업체 지원 명시), blocked(미지원 명시), unknown(해당 OS 정보 없음). 열린 열거형입니다.

supportedblockedunknown
notestring
null 허용
—
hotspotboolean
null 허용

다른 기기와 Wi-Fi 공유 기능입니다. null은 미선언이며 false와 다릅니다.

kyc_requiredboolean
null 허용

사용 전에 신원 확인(KYC)이 필요합니다. null은 미명시를 뜻합니다.

smsboolean
null 허용

SMS입니다. false이면 문자 수신을 지원하지 않아 은행 OTP도 받을 수 없습니다. null은 미명시입니다.

voiceboolean
null 허용

음성 통화입니다. false는 공급업체가 미지원을 명시한 것이며 데이터 전용 회선은 전화를 받을 수 없습니다. null은 미선언입니다.

fupobject
null 허용

고속 데이터 소진 후 동작입니다. null은 속도 제한 정책 미선언입니다.

behaviorstring
null 허용

throttle(속도 제한), cutoff(연결 종료), null(미선언). 열린 열거형입니다.

throttlecutoffnull
high_speed_bytesinteger · int64
null 허용

속도 제한 전 고속 데이터 바이트 수입니다.

notestring
null 허용
—
speed_kbpsinteger
null 허용

고속 용량을 소진한 이후 속도(kbps)입니다. 연결이 끊기거나 정보가 미명시이면 null입니다. 전체 요금제 속도 상한인 network.speed_cap_kbps와 다릅니다.

marketstring

요금제의 공급 시스템으로 확장 가능한 열거형이므로 알 수 없는 값은 무시해야 합니다. 재고 유무와 판매 후 작업을 결정합니다. domestic은 키트를 판매하며 충전·취소·사용량 조회를 지원하고, international은 요청 시 공급업체에 주문하므로 재고가 없습니다.

domesticinternational
namestring
—
name_enstring
null 허용

영문 이름입니다. 제공되지 않으면 null입니다.

networkobject

패키지가 사용하는 네트워크입니다.

apnstring
null 허용

자동 설정이 되지 않는 일부 Android 기기에 수동 입력할 APN입니다. 설치 안내에 필요한 정보이며 없으면 고객 지원 문의로 이어집니다. null은 미명시입니다.

carriersobject[]

요금제가 사용하는 통신사입니다. 어느 무선망을 쓰는지에 대한 eSIM 사양이며 공급업체가 아닙니다.

namestring
—
generationsstring[]
null 허용

사용 가능한 이동통신 세대입니다(["4g", "5g"]). null은 미명시이며 2G 전용이라는 뜻이 아닙니다.

speed_cap_kbpsinteger
null 허용

전체 요금제의 속도 상한(kbps)으로, 고속 용량 소진 후 적용하는 fup.speed_kbps와 다릅니다.

priceobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
tagsstring[]

소문자와 하이픈으로 정규화한 상품 태그로, 키의 카탈로그 제한에도 사용됩니다. 현재 태그가 있는 요금제가 없으므로 판매자가 태그를 추가하기 전까지 ?tag=는 빈 목록을 반환합니다.

validityobject

요금제 유효기간과 시작 시점입니다.

daysinteger
—
install_by_daysinteger
null 허용

구매 후 QR 스캔 또는 eSIM 설치가 가능한 기한(일)입니다. null은 미명시이며 언제든 설치 가능하다는 뜻이 아닙니다.

labelstring
—
reclaim_on_expiryboolean
null 허용

만료 시 공급업체가 회선을 회수하여 충전과 요금제 변경이 불가능해집니다. 갱신 판매 시 기한을 알아야 합니다. null은 미선언입니다.

starts_whenstring
null 허용

기간 시작 시점: on_install(설치), on_purchase(구매), null. 만료일 계산에 중요한 정보이며 null이면 특정 만료일을 약속하지 마세요.

on_installon_purchasenull
unused_valid_daysinteger
null 허용

구매 후 사용하지 않고 보관할 수 있는 만료 전 일수입니다. null은 미명시이며 무기한이 아닙니다. 보관 유효기간은 설치 기한인 install_by_days와 별개입니다.

modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
next_cursorstring
null 허용

다음 페이지를 조회할 때 값을 그대로 전달하세요. null만이 종료 신호이며, page_size보다 적게 반환되어도 다음 페이지가 있을 수 있습니다.

page_sizeinteger
null 허용

서버가 이 페이지에 적용한 최대 레코드 수이며 상한이 반영된 값입니다. 실제 items 항목 수가 아닙니다.

totalinteger · int64
null 허용

필터에 맞는 총 패키지 수입니다. 국제 또는 market 미지정 등 계산 비용이 크면 null입니다. 키 제한과 조직 가격 검사 전에 계산하므로 구매 가능 수가 아닌 카탈로그 일치 수입니다.

warningsobject[]

목록에 대한 설명이며 특이사항이 없으면 비어 있습니다. 고정 코드: filtered_by_key_package_filters, filtered_by_provider_scope, no_price_for_org, price_filter_removed_rows, provider_disabled, capability_missing. 각각 키 제한, 조직 공급원 범위, 미설정 가격, 가격 필터, 공급원 비활성, 기능 미선언을 뜻합니다. count는 해당 행 수이며 수치화할 수 없으면 null입니다.

codestring
—
filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing
countinteger · int64
null 허용
—
messagestring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failedinternal.invalid_filtercatalog.cursor_mismatch
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="GET"
REQ_PATH="/v1/catalog/packages"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "items": [
      {
        "capabilities": {
          "cancel": {
            "restriction": "code_not_disclosed",
            "supported": true
          },
          "plan_change": {
            "policy": {
              "note": "<note>",
              "requires_cancel_first": true,
              "stacking": true
            },
            "supported": true
          },
          "usage_check": {
            "supported": true
          }
        },
        "code": "TKE-VN-30D-X3K9F",
        "coverage": {
          "countries": [
            "CN"
          ],
          "country_count": 1,
          "group": "country",
          "region": "<region>"
        },
        "data": {
          "daily_bytes": 1073741824,
          "label": "1GB/ngày",
          "model": "daily",
          "reset": {
            "at": "00:00",
            "note": "<note>",
            "timezone": "UTC+8"
          },
          "total_bytes": null
        },
        "description": "<description>",
        "description_en": "<description_en>",
        "features": {
          "apps": [
            {
              "android": "blocked",
              "app": "tiktok",
              "ios": "supported",
              "note": "<note>"
            }
          ],
          "hotspot": true,
          "kyc_required": false,
          "sms": false,
          "voice": false
        },
        "fup": {
          "behavior": "throttle",
          "high_speed_bytes": 1048576,
          "note": "<note>",
          "speed_kbps": 128
        },
        "market": "international",
        "name": "eSIM Trung Quốc Mainland Multi - 1GB/ngày - 24 ngày",
        "name_en": "<name_en>",
        "network": {
          "apn": "e-ideas",
          "carriers": [
            {
              "name": "China Unicom"
            }
          ],
          "generations": [
            "4g",
            "5g"
          ],
          "speed_cap_kbps": 1
        },
        "price": {
          "amount": 273000,
          "currency": "VND"
        },
        "tags": [],
        "validity": {
          "days": 24,
          "install_by_days": 365,
          "label": "24 ngày",
          "reclaim_on_expiry": true,
          "starts_when": "on_install",
          "unused_valid_days": 1
        }
      }
    ],
    "mode": "live",
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNy0xMVQwODoxMjozMFoifQ",
    "page_size": 50,
    "total": 128,
    "warnings": []
  },
  "ok": true
}
GET/v1/catalog/packages/{code}

요금제 한 건 조회

scope catalog:readget_catalog_packages__code_

필요한 권한 범위: catalog:read.

공개 코드로 정확히 하나의 요금제 상세를 조회합니다. data는 GET /v1/catalog/packages의 항목과 같은 필드에 mode만 추가됩니다. 상세 전용 필드가 없으므로 Package 타입 하나로 처리할 수 있습니다.

보이지 않는 코드는 원인과 관계없이 partner.package_not_visible(404)를 반환합니다. 코드 없음, 키의 카탈로그 제한, 조직 가격 없음, 공급 연결 끊김을 구분하면 외부인이 전체 카탈로그를 추측할 수 있기 때문입니다.

경로 매개변수 (1)

codestring필수

카탈로그의 code인 요금제 코드(public_sku)입니다. 공급업체가 바뀌어도 불변이므로 대사 키로 저장할 수 있습니다. 키 제한 범위 밖이거나 조직 가격이 없는 코드는 존재하지 않는 코드와 같은 404 partner.package_not_visible을 반환합니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
partner.package_not_visible
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
CODE="THAY-BANG-CODE"

METHOD="GET"
REQ_PATH="/v1/catalog/packages/$CODE"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": null,
  "ok": true
}

주문

주문 생성 및 대사

GET/v1/orders

주문 목록 조회(조회 기반 대사)

scope orders:readget_orders

필요한 권한 범위: orders:read.

쿼리 매개변수 (5)

page_sizeinteger선택기본값 50min 1max 200

페이지당 레코드 수입니다. 기본 50, 최대 200이며 더 큰 값을 보내면 오류 없이 200으로 제한됩니다.

statusstring선택
허용 값:completedpending_providerprovider_failedcancelled

주문 상태로 필터링합니다. 목록에 없는 값은 internal.invalid_filter(400)를 반환합니다.

external_refstring선택

주문 확정 시 보낸 참조 코드로 필터링합니다(정확히 일치).

updated_sincestring · date-time선택

ISO-8601 UTC 형식입니다. updated_at >= updated_since인 레코드를 오름차순으로 반환하는 표준 조회 기반 대사 경로(§8.5)입니다. 웹훅을 놓쳐도 상태를 복원할 수 있습니다. 형식이 틀리면 internal.invalid_filter(400)입니다.

cursorstring선택

다음 페이지 커서로 이전 next_cursor를 그대로 보내세요. 잘못된 커서는 오류 없이 목록 처음부터 다시 읽습니다. 이는 인코딩 변경 시 호환성을 유지하기 위한 동작입니다. 첫 페이지 무한 반복을 막으려면 다음 커서가 보낸 값과 다른지 확인하세요.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
itemsobject[]
—
created_atstring · date-time
—
external_refstring
null 허용
—
fulfillment_statusstring
—
fulfilledpartially_fulfilledunfulfilled
idstring · uuid
—
item_countinteger · int64
—
netobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
public_refstring
null 허용
—
refundedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
statusstring
—
totalobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
updated_atstring · date-time
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
next_cursorstring
null 허용

다음 페이지를 조회할 때 값을 그대로 전달하세요. null만이 종료 신호이며, page_size보다 적게 반환되어도 다음 페이지가 있을 수 있습니다.

page_sizeinteger
null 허용

서버가 이 페이지에 적용한 최대 레코드 수이며 상한이 반영된 값입니다. 실제 items 항목 수가 아닙니다.

totalinteger · int64
null 허용

필터에 맞는 레코드 수입니다. 집계 비용이 너무 높으면 null입니다. 마지막 페이지 판단에는 이 값이 아닌 next_cursor를 사용하세요.

warningsobject[]

목록에 대한 설명이며 특이사항이 없으면 비어 있습니다. 고정 코드: filtered_by_key_package_filters, filtered_by_provider_scope, no_price_for_org, price_filter_removed_rows, provider_disabled, capability_missing. 각각 키 제한, 조직 공급원 범위, 미설정 가격, 가격 필터, 공급원 비활성, 기능 미선언을 뜻합니다. count는 해당 행 수이며 수치화할 수 없으면 null입니다.

codestring
—
filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing
countinteger · int64
null 허용
—
messagestring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failedinternal.invalid_filter
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="GET"
REQ_PATH="/v1/orders"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "items": [
      {
        "created_at": "2026-07-11T08:12:30Z",
        "external_ref": "po-1001",
        "fulfillment_status": "fulfilled",
        "id": "018f2b00-0000-7000-8000-00000000009a",
        "item_count": 2,
        "net": {
          "amount": 273000,
          "currency": "VND"
        },
        "public_ref": "TKE-DH-2A4C7Q",
        "refunded": {
          "amount": 273000,
          "currency": "VND"
        },
        "status": "completed",
        "total": {
          "amount": 273000,
          "currency": "VND"
        },
        "updated_at": "2026-07-11T09:03:11Z"
      }
    ],
    "mode": "live",
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNy0xMVQwODoxMjozMFoifQ",
    "page_size": 50,
    "total": 128,
    "warnings": []
  },
  "ok": true
}
POST/v1/orders

주문 생성(직접 구매)

scope orders:writeIdempotency-Key 필수post_orders

필요한 권한 범위: orders:write.

헤더 (1)

Idempotency-Keystring필수

클라이언트 생성 문자열입니다. 같은 키와 본문은 한 번만 실행되며 재시도는 원래 결과를 반환합니다. api_key_id 단위(§11)이므로 다른 키는 별개 명령입니다.

요청 본문필수application/json

필드유형제약 조건설명
external_refstring
null 허용

응답 및 웹훅에 그대로 반환되는 자체 참조 코드입니다.

includestring[]
null 허용

추가 블록 요청: ["lpa"] 또는 ["lpa","qr"]. 생략하면 items[].install은 null이고 모든 eSIM의 취소 권리가 유지됩니다. 코드 반환은 공개를 의미합니다. lpa를 요청하면 주문 시점에 취소 가능 기간이 종료됩니다.

invoice_buyerobject
null 허용

회원 키 전용입니다. 생략하면 조직이나 베트남 법인 없이 소비자 흐름을 사용합니다. 입력 시 기존 개인 검증인 full_name, 12자리 개인 tax_code 및 checkout의 address/email 규칙을 적용합니다. 기업 청구서는 구매 후 요청합니다. 테스트 모드는 스냅샷만 검증하며 MISA 청구서를 발행하지 않습니다.

addressstring
null 허용
—
emailstring
null 허용
—
full_namestring
null 허용
—
legal_namestring
null 허용
—
tax_codestring
null 허용
—
itemsobject[]
필수

구매할 요금제 및 수량 목록입니다(eSIM 최대 50개).

package_codestring
필수

자사 카탈로그의 요금제 코드인 public_sku입니다. 공급업체가 변경되어도 요금제별로 불변이므로 대사 키로 안전하게 사용할 수 있습니다. 예시는 형식만 맞고 실제로 작동하지 않으니 GET /v1/catalog/packages에서 실제 코드를 조회하세요.

quantityinteger
필수

이 요금제로 구매할 eSIM 수량입니다(≥ 1).

응답

200기존 주문 결과 재전송

24시간 내 같은 Idempotency-Key와 본문으로 재실행하면 원래 본문을 그대로 반환하고 중복 결제하지 않으며 Idempotent-Replayed: true를 추가합니다. 새 확정은 201이므로 상태 코드만으로 성공을 구분하지 마세요.

필드유형제약 조건설명
dataobject
필수

파트너 주문입니다.

created_atstring · date-time
—
external_refstring
null 허용
—
fulfillment_statusstring

실제로 제공된 수량으로, status와 별개입니다. eSIM 3개 중 2개가 제공되고 1개가 실패해도 status=completed이며, fulfillment_status=partially_fulfilled, refunded_amount > 0이 됩니다.

fulfilledpartially_fulfilledunfulfilled
idstring · uuid

주문 ID(UUID)입니다.

idempotent_replayboolean
null 허용

이전 성공 요청의 멱등 재실행이면 참입니다.

invoice_nostring
null 허용

발행 후 부여되는 청구서 번호입니다.

invoice_statusstring

주문의 VAT 청구서 상태입니다. none은 아직 미발행입니다.

nonependingissuingissuedfailedcancelled
itemsobject[]
—
cancellableobject

eSIM 취소 가능 여부와 불가 사유입니다. POST /v1/esims/{order_item_id}/cancel 호출 전에 읽으세요. 답변이므로 항상 존재하고 null이 아닙니다.

allowedboolean
—
disclosed_atstring · date-time
null 허용

코드가 시스템 밖으로 공개된 시각(ISO-8601 UTC)입니다. 아무도 보지 않았으면 null이며, 고객이 수신하지 못했는데 취소 불가라고 주장할 때 확인할 기록입니다.

reasonstring
null 허용

취소 불가 사유: code_disclosed(전송 또는 표시됨), wrong_status(취소 불가 상태), expired(만료), not_supported(취소 기능 미선언). allowed = true이면 null입니다. esim.not_cancellable의 meta.reason과 같은 코드입니다.

code_disclosedwrong_statusexpirednot_supportednull
external_refstring
null 허용

주문 시 보낸 external_ref로 eSIM을 주문과 연결합니다. 참조 없이 주문했으면 null입니다.

iccidstring
null 허용
—
idstring · uuid

모든 /v1/esims/{id}/… 경로에 사용하는 eSIM ID이며 웹훅의 order_item_id와 같습니다. ICCID만 있으면 GET /v1/esims?iccid=…로 id를 조회하세요.

installobject
null 허용

이 eSIM 설치에 필요한 모든 정보입니다. null은 include=lpa로 요청하지 않았다는 뜻입니다. 아직 설치 코드가 없는 경우와는 다르며, 그 경우 블록은 존재하고 lpa: null입니다.

이 블록이 포함된 응답에는 항상 Cache-Control: no-store가 설정됩니다.

android_urlstring
null 허용
—
apnstring
null 허용

기기가 자동 구성하지 못할 때 입력할 APN입니다. 현재 버전에서는 항상 null이며 알 수 없다는 뜻이지 불필요하다는 뜻이 아닙니다.

ios_urlstring
null 허용
—
lpastring
null 허용

eSIM 활성화 코드이며 판매 가능한 비밀 정보입니다. 이 문자열만으로 QR 코드를 생성할 수 있습니다.

matching_idstring
null 허용
—
qr_code_base64string
null 허용

QR 코드의 PNG data-URI입니다. 상세 엔드포인트에서 include=qr을 요청한 경우에만 포함되며 목록에는 포함되지 않습니다(행당 약 11KB, 100행이면 약 1.1MB). 목록에서는 lpa로 QR 코드를 생성하세요.

smdp_addressstring
null 허용
—
msisdnstring
null 허용
—
order_idstring · uuid
—
package_codestring
null 허용

카탈로그의 요금제 코드로, eSIM을 카탈로그와 연결하는 키입니다. 원본 요금제가 카탈로그에서 제거된 경우에만 null입니다.

package_namestring

변경될 수 있는 사람이 읽는 표시 이름입니다. 대사 키로 사용하지 마세요.

planobject
null 허용

이 eSIM에 현재 적용된 요금제입니다.

current_codestring
—
expires_atstring · date-time
null 허용
—
started_atstring · date-time
null 허용
—
smdp_statusstring
null 허용
—
statusstring
—
updated_atstring · date-time
—
usageobject
null 허용

eSIM 데이터 사용량 통계입니다. 조회한 적이 없으면 updated_at = null입니다。

expires_atstring · date-time
null 허용
—
remaining_bytesinteger · int64
null 허용
—
sourcestring
null 허용
—
pollwebhook
total_bytesinteger · int64
null 허용
—
updated_atstring · date-time
null 허용
—
used_bytesinteger · int64
null 허용
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
money_settledboolean

주문 금액이 정산되었는지 여부입니다(진행 중인 환불 없음). false이면 청구서를 발행할 수 없습니다.

netobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
public_refstring
null 허용

지원 요청에 사용할 짧고 읽기 쉬운 주문 코드입니다.

refundedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
statusstring
—
completedpending_providerprovider_failedcancelled
totalobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
okboolean
필수
—
true
201성공

성공

필드유형제약 조건설명
dataobject
필수

파트너 주문입니다.

created_atstring · date-time
—
external_refstring
null 허용
—
fulfillment_statusstring

실제로 제공된 수량으로, status와 별개입니다. eSIM 3개 중 2개가 제공되고 1개가 실패해도 status=completed이며, fulfillment_status=partially_fulfilled, refunded_amount > 0이 됩니다.

fulfilledpartially_fulfilledunfulfilled
idstring · uuid

주문 ID(UUID)입니다.

idempotent_replayboolean
null 허용

이전 성공 요청의 멱등 재실행이면 참입니다.

invoice_nostring
null 허용

발행 후 부여되는 청구서 번호입니다.

invoice_statusstring

주문의 VAT 청구서 상태입니다. none은 아직 미발행입니다.

nonependingissuingissuedfailedcancelled
itemsobject[]
—
cancellableobject

eSIM 취소 가능 여부와 불가 사유입니다. POST /v1/esims/{order_item_id}/cancel 호출 전에 읽으세요. 답변이므로 항상 존재하고 null이 아닙니다.

allowedboolean
—
disclosed_atstring · date-time
null 허용

코드가 시스템 밖으로 공개된 시각(ISO-8601 UTC)입니다. 아무도 보지 않았으면 null이며, 고객이 수신하지 못했는데 취소 불가라고 주장할 때 확인할 기록입니다.

reasonstring
null 허용

취소 불가 사유: code_disclosed(전송 또는 표시됨), wrong_status(취소 불가 상태), expired(만료), not_supported(취소 기능 미선언). allowed = true이면 null입니다. esim.not_cancellable의 meta.reason과 같은 코드입니다.

code_disclosedwrong_statusexpirednot_supportednull
external_refstring
null 허용

주문 시 보낸 external_ref로 eSIM을 주문과 연결합니다. 참조 없이 주문했으면 null입니다.

iccidstring
null 허용
—
idstring · uuid

모든 /v1/esims/{id}/… 경로에 사용하는 eSIM ID이며 웹훅의 order_item_id와 같습니다. ICCID만 있으면 GET /v1/esims?iccid=…로 id를 조회하세요.

installobject
null 허용

이 eSIM 설치에 필요한 모든 정보입니다. null은 include=lpa로 요청하지 않았다는 뜻입니다. 아직 설치 코드가 없는 경우와는 다르며, 그 경우 블록은 존재하고 lpa: null입니다.

이 블록이 포함된 응답에는 항상 Cache-Control: no-store가 설정됩니다.

android_urlstring
null 허용
—
apnstring
null 허용

기기가 자동 구성하지 못할 때 입력할 APN입니다. 현재 버전에서는 항상 null이며 알 수 없다는 뜻이지 불필요하다는 뜻이 아닙니다.

ios_urlstring
null 허용
—
lpastring
null 허용

eSIM 활성화 코드이며 판매 가능한 비밀 정보입니다. 이 문자열만으로 QR 코드를 생성할 수 있습니다.

matching_idstring
null 허용
—
qr_code_base64string
null 허용

QR 코드의 PNG data-URI입니다. 상세 엔드포인트에서 include=qr을 요청한 경우에만 포함되며 목록에는 포함되지 않습니다(행당 약 11KB, 100행이면 약 1.1MB). 목록에서는 lpa로 QR 코드를 생성하세요.

smdp_addressstring
null 허용
—
msisdnstring
null 허용
—
order_idstring · uuid
—
package_codestring
null 허용

카탈로그의 요금제 코드로, eSIM을 카탈로그와 연결하는 키입니다. 원본 요금제가 카탈로그에서 제거된 경우에만 null입니다.

package_namestring

변경될 수 있는 사람이 읽는 표시 이름입니다. 대사 키로 사용하지 마세요.

planobject
null 허용

이 eSIM에 현재 적용된 요금제입니다.

current_codestring
—
expires_atstring · date-time
null 허용
—
started_atstring · date-time
null 허용
—
smdp_statusstring
null 허용
—
statusstring
—
updated_atstring · date-time
—
usageobject
null 허용

eSIM 데이터 사용량 통계입니다. 조회한 적이 없으면 updated_at = null입니다。

expires_atstring · date-time
null 허용
—
remaining_bytesinteger · int64
null 허용
—
sourcestring
null 허용
—
pollwebhook
total_bytesinteger · int64
null 허용
—
updated_atstring · date-time
null 허용
—
used_bytesinteger · int64
null 허용
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
money_settledboolean

주문 금액이 정산되었는지 여부입니다(진행 중인 환불 없음). false이면 청구서를 발행할 수 없습니다.

netobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
public_refstring
null 허용

지원 요청에 사용할 짧고 읽기 쉬운 주문 코드입니다.

refundedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
statusstring
—
completedpending_providerprovider_failedcancelled
totalobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failedinternal.invalid_filter
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
402
wallet.insufficient_balance
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
partner.package_not_visible
409
esim.out_of_stockidempotency.conflict
422
invoice.buyer_invalid
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="POST"
REQ_PATH="/v1/orders"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"external_ref":"po-1001","include":["lpa"],"invoice_buyer":{"address":"<address>","email":"<email>","full_name":"<full_name>","legal_name":"<legal_name>","tax_code":"TKE-VN-30D-X3K9F"},"items":[{"package_code":"TKE-VN-30D-X3K9F","quantity":2}]}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "created_at": "2026-07-11T08:12:30Z",
    "external_ref": "po-1001",
    "fulfillment_status": "fulfilled",
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "idempotent_replay": true,
    "invoice_no": "<invoice_no>",
    "invoice_status": "none",
    "items": [
      {
        "cancellable": {
          "allowed": true,
          "disclosed_at": "2026-07-11T08:12:30Z",
          "reason": "code_disclosed"
        },
        "external_ref": "po-1001",
        "iccid": "8984012345678901234",
        "id": "018f2b00-0000-7000-8000-00000000009a",
        "install": {
          "android_url": "https://esimsetup.google.com/esim_qrcode_provisioning?data=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
          "apn": "<apn>",
          "ios_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
          "lpa": "LPA:1$smdp.example.com$MATCH-ABC123",
          "matching_id": "MATCH-ABC123",
          "qr_code_base64": "<PNG base64 — đã rút gọn>",
          "smdp_address": "smdp.example.com"
        },
        "msisdn": "+84900000000",
        "order_id": "018f2b00-0000-7000-8000-00000000009a",
        "package_code": "TKE-VN-30D-X3K9F",
        "package_name": "Tên gói hiển thị",
        "plan": {
          "current_code": "TKE-VN-30D-X3K9F",
          "expires_at": "2026-08-10T00:00:00Z",
          "started_at": "2026-07-11T08:12:30Z"
        },
        "smdp_status": "released",
        "status": "delivered",
        "updated_at": "2026-07-11T09:03:11Z",
        "usage": {
          "expires_at": "2026-08-10T00:00:00Z",
          "remaining_bytes": 5368709120,
          "source": "poll",
          "total_bytes": 5368709120,
          "updated_at": "2026-07-11T09:03:11Z",
          "used_bytes": 5368709120
        }
      }
    ],
    "mode": "live",
    "money_settled": true,
    "net": {
      "amount": 273000,
      "currency": "VND"
    },
    "public_ref": "TKE-DH-2A4C7Q",
    "refunded": {
      "amount": 273000,
      "currency": "VND"
    },
    "status": "completed",
    "total": {
      "amount": 273000,
      "currency": "VND"
    }
  },
  "ok": true
}

요청 본문 예시

json
{
  "external_ref": "po-1001",
  "include": [
    "lpa"
  ],
  "invoice_buyer": {
    "address": "<address>",
    "email": "<email>",
    "full_name": "<full_name>",
    "legal_name": "<legal_name>",
    "tax_code": "TKE-VN-30D-X3K9F"
  },
  "items": [
    {
      "package_code": "TKE-VN-30D-X3K9F",
      "quantity": 2
    }
  ]
}
GET/v1/orders/{id}

주문 상세 및 eSIM

scope orders:readget_orders__id_

필요한 권한 범위: orders:read.

경로 매개변수 (1)

idstring필수

주문 ID(UUID)입니다.

쿼리 매개변수 (1)

includestring선택

쉼표로 추가 블록을 요청합니다. lpa는 install, qr는 상세 조회에서만 install.qr_code_base64를 추가합니다. 반환된 코드는 공개된 것으로 간주하여 해당 eSIM의 취소가 영구 차단됩니다(cancellable.allowed = false, reason = code_disclosed). 즉시 전달할 때만 요청하고 사전 주문에서는 생략해 취소 권리를 유지하세요. 알 수 없는 값은 internal.invalid_filter(400, field: "include")입니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수

파트너 주문입니다.

created_atstring · date-time
—
external_refstring
null 허용
—
fulfillment_statusstring

실제로 제공된 수량으로, status와 별개입니다. eSIM 3개 중 2개가 제공되고 1개가 실패해도 status=completed이며, fulfillment_status=partially_fulfilled, refunded_amount > 0이 됩니다.

fulfilledpartially_fulfilledunfulfilled
idstring · uuid

주문 ID(UUID)입니다.

idempotent_replayboolean
null 허용

이전 성공 요청의 멱등 재실행이면 참입니다.

invoice_nostring
null 허용

발행 후 부여되는 청구서 번호입니다.

invoice_statusstring

주문의 VAT 청구서 상태입니다. none은 아직 미발행입니다.

nonependingissuingissuedfailedcancelled
itemsobject[]
—
cancellableobject

eSIM 취소 가능 여부와 불가 사유입니다. POST /v1/esims/{order_item_id}/cancel 호출 전에 읽으세요. 답변이므로 항상 존재하고 null이 아닙니다.

allowedboolean
—
disclosed_atstring · date-time
null 허용

코드가 시스템 밖으로 공개된 시각(ISO-8601 UTC)입니다. 아무도 보지 않았으면 null이며, 고객이 수신하지 못했는데 취소 불가라고 주장할 때 확인할 기록입니다.

reasonstring
null 허용

취소 불가 사유: code_disclosed(전송 또는 표시됨), wrong_status(취소 불가 상태), expired(만료), not_supported(취소 기능 미선언). allowed = true이면 null입니다. esim.not_cancellable의 meta.reason과 같은 코드입니다.

code_disclosedwrong_statusexpirednot_supportednull
external_refstring
null 허용

주문 시 보낸 external_ref로 eSIM을 주문과 연결합니다. 참조 없이 주문했으면 null입니다.

iccidstring
null 허용
—
idstring · uuid

모든 /v1/esims/{id}/… 경로에 사용하는 eSIM ID이며 웹훅의 order_item_id와 같습니다. ICCID만 있으면 GET /v1/esims?iccid=…로 id를 조회하세요.

installobject
null 허용

이 eSIM 설치에 필요한 모든 정보입니다. null은 include=lpa로 요청하지 않았다는 뜻입니다. 아직 설치 코드가 없는 경우와는 다르며, 그 경우 블록은 존재하고 lpa: null입니다.

이 블록이 포함된 응답에는 항상 Cache-Control: no-store가 설정됩니다.

android_urlstring
null 허용
—
apnstring
null 허용

기기가 자동 구성하지 못할 때 입력할 APN입니다. 현재 버전에서는 항상 null이며 알 수 없다는 뜻이지 불필요하다는 뜻이 아닙니다.

ios_urlstring
null 허용
—
lpastring
null 허용

eSIM 활성화 코드이며 판매 가능한 비밀 정보입니다. 이 문자열만으로 QR 코드를 생성할 수 있습니다.

matching_idstring
null 허용
—
qr_code_base64string
null 허용

QR 코드의 PNG data-URI입니다. 상세 엔드포인트에서 include=qr을 요청한 경우에만 포함되며 목록에는 포함되지 않습니다(행당 약 11KB, 100행이면 약 1.1MB). 목록에서는 lpa로 QR 코드를 생성하세요.

smdp_addressstring
null 허용
—
msisdnstring
null 허용
—
order_idstring · uuid
—
package_codestring
null 허용

카탈로그의 요금제 코드로, eSIM을 카탈로그와 연결하는 키입니다. 원본 요금제가 카탈로그에서 제거된 경우에만 null입니다.

package_namestring

변경될 수 있는 사람이 읽는 표시 이름입니다. 대사 키로 사용하지 마세요.

planobject
null 허용

이 eSIM에 현재 적용된 요금제입니다.

current_codestring
—
expires_atstring · date-time
null 허용
—
started_atstring · date-time
null 허용
—
smdp_statusstring
null 허용
—
statusstring
—
updated_atstring · date-time
—
usageobject
null 허용

eSIM 데이터 사용량 통계입니다. 조회한 적이 없으면 updated_at = null입니다。

expires_atstring · date-time
null 허용
—
remaining_bytesinteger · int64
null 허용
—
sourcestring
null 허용
—
pollwebhook
total_bytesinteger · int64
null 허용
—
updated_atstring · date-time
null 허용
—
used_bytesinteger · int64
null 허용
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
money_settledboolean

주문 금액이 정산되었는지 여부입니다(진행 중인 환불 없음). false이면 청구서를 발행할 수 없습니다.

netobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
public_refstring
null 허용

지원 요청에 사용할 짧고 읽기 쉬운 주문 코드입니다.

refundedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
statusstring
—
completedpending_providerprovider_failedcancelled
totalobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failedinternal.invalid_filter
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
internal.not_found
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="GET"
REQ_PATH="/v1/orders/$ID"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "created_at": "2026-07-11T08:12:30Z",
    "external_ref": "po-1001",
    "fulfillment_status": "fulfilled",
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "idempotent_replay": true,
    "invoice_no": "<invoice_no>",
    "invoice_status": "none",
    "items": [
      {
        "cancellable": {
          "allowed": true,
          "disclosed_at": "2026-07-11T08:12:30Z",
          "reason": "code_disclosed"
        },
        "external_ref": "po-1001",
        "iccid": "8984012345678901234",
        "id": "018f2b00-0000-7000-8000-00000000009a",
        "install": {
          "android_url": "https://esimsetup.google.com/esim_qrcode_provisioning?data=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
          "apn": "<apn>",
          "ios_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
          "lpa": "LPA:1$smdp.example.com$MATCH-ABC123",
          "matching_id": "MATCH-ABC123",
          "qr_code_base64": "<PNG base64 — đã rút gọn>",
          "smdp_address": "smdp.example.com"
        },
        "msisdn": "+84900000000",
        "order_id": "018f2b00-0000-7000-8000-00000000009a",
        "package_code": "TKE-VN-30D-X3K9F",
        "package_name": "Tên gói hiển thị",
        "plan": {
          "current_code": "TKE-VN-30D-X3K9F",
          "expires_at": "2026-08-10T00:00:00Z",
          "started_at": "2026-07-11T08:12:30Z"
        },
        "smdp_status": "released",
        "status": "delivered",
        "updated_at": "2026-07-11T09:03:11Z",
        "usage": {
          "expires_at": "2026-08-10T00:00:00Z",
          "remaining_bytes": 5368709120,
          "source": "poll",
          "total_bytes": 5368709120,
          "updated_at": "2026-07-11T09:03:11Z",
          "used_bytes": 5368709120
        }
      }
    ],
    "mode": "live",
    "money_settled": true,
    "net": {
      "amount": 273000,
      "currency": "VND"
    },
    "public_ref": "TKE-DH-2A4C7Q",
    "refunded": {
      "amount": 273000,
      "currency": "VND"
    },
    "status": "completed",
    "total": {
      "amount": 273000,
      "currency": "VND"
    }
  },
  "ok": true
}
POST/v1/orders/{id}/resend

주문의 이메일/QR 재전송

scope orders:writepost_orders__id__resend

필요한 권한 범위: orders:write.

경로 매개변수 (1)

idstring필수

주문 ID(UUID)입니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
idstring · uuid

방금 다시 전송한 주문의 ID입니다.

modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
resent_atstring · date-time
—
resent_tostring

일부 가린 수신 주소입니다. 파트너 API는 고객의 전체 이메일을 다시 노출하지 않습니다.

okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
409
order.not_deliverable
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="POST"
REQ_PATH="/v1/orders/$ID/resend"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "mode": "live",
    "resent_at": "2026-07-11T08:12:30Z",
    "resent_to": "ab***@x.com"
  },
  "ok": true
}
POST/v1/orders/bulk

여러 줄 일괄 주문(줄마다 주문 한 건)

scope orders:writeIdempotency-Key 필수post_orders_bulk

필요한 권한 범위: orders:write.

여러 줄을 일괄 주문합니다. 각 줄은 POST /v1/orders처럼 quote→commit을 거치는 독립 주문 한 건을 생성합니다.

부분 성공 방식으로 실패한 줄은 다른 줄을 막지 않으며 성공한 줄의 금액만 차감합니다. 품절, 잔액 부족, 판매 불가, 남용 방지 제한 등은 lines[].error에 기록하고 배치의 HTTP 상태는 유지합니다. 도중에 잔액이 부족해도 더 저렴한 줄은 구매 가능하므로 이후 줄도 시도합니다.

상태는 항상 200입니다. 단일 신규 리소스가 아닌 줄별 혼합 결과이므로 201이 아니며 요청 안에서 완료되므로 202 및 폴링도 없습니다.

동일 Idempotency-Key와 동일 본문으로 재호출하면 기존 배치를 불러와 완료 결과를 그대로 반환하고 pending 줄만 이어서 처리하며 중복 차감하지 않습니다. 실행한 줄이 없으면 Idempotent-Replayed: true입니다. 같은 키에 다른 본문은 idempotency.conflict입니다.

같은 조직이 10분 내 동일 내용으로 두 배치를 보내면 키가 달라도 실수로 중복 클릭한 것으로 간주하여 이전 배치와 Idempotent-Replayed: true를 반환하고 추가 차감하지 않습니다. 실제로 같은 상품을 다시 구매하려면 서명 본문에 포함된 external_ref를 변경하세요. 배치 구분과 이후 대사에도 사용됩니다. 모든 줄이 실패해 주문이 하나도 생성되지 않은 배치는 차단하지 않으며 10분이 지나면 다시 정상 주문할 수 있습니다.

줄별 참조는 lines[].external_ref, 배치 참조는 응답 상단에 그대로 반환합니다.

응답에는 LPA/QR이 없으며 각 order_id로 GET /v1/orders/{order_id}를 조회하세요.

헤더 (1)

Idempotency-Keystring필수

클라이언트 생성 문자열입니다. 같은 키와 본문은 한 번만 실행되며 재시도는 원래 결과를 반환합니다. api_key_id 단위(§11)이므로 다른 키는 별개 명령입니다.

요청 본문필수application/json

필드유형제약 조건설명
external_refstring
null 허용

전체 일괄 요청의 참조 코드이며 일괄 응답에 반환됩니다.

itemsobject[]
필수

각 항목은 독립 주문 하나를 생성합니다. 행 수 한도는 설정 가능하며 기본 100입니다. 초과 시 internal.limit_exceeded(422)를 반환합니다.

external_refstring
null 허용

이 줄만의 참조 코드이며 해당 줄의 결과에 그대로 반환됩니다.

package_codestring
필수

자사 카탈로그의 요금제 코드인 public_sku입니다. 공급업체가 변경되어도 요금제별로 불변이므로 대사 키로 안전하게 사용할 수 있습니다. 예시는 형식만 맞고 실제로 작동하지 않으니 GET /v1/catalog/packages에서 실제 코드를 조회하세요.

quantityinteger
필수
—

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수

일괄 주문 결과입니다. 부분 성공 방식으로 실패한 줄은 다른 줄을 막지 않고 done 줄만 차감합니다. LPA/QR은 포함하지 않으며 각 order_id로 GET /v1/orders/{id}에서 조회하세요.

created_atstring · date-time
—
external_refstring
null 허용
—
failed_linesinteger
—
idstring · uuid
—
itemsobject[]
—
chargedobject
null 허용
—
errorobject
null 허용

실제 domain.reason 오류 코드입니다. 예: wallet.insufficient_balance, esim.out_of_stock, order.package_not_allowed, wallet.velocity_exceeded.

codestring
—
messagestring
—
external_refstring
null 허용
—
line_nointeger

제출한 순서 그대로 1부터 시작합니다.

order_idstring · uuid
null 허용
—
package_codestring
—
quantityinteger
—
statusstring
—
pendingdonefailed
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
statusstring

pending은 중단되어 아직 실행되지 않은 행이 있다는 뜻입니다. 같은 Idempotency-Key로 다시 호출해 나머지를 완료하세요.

pendingcompletedpartialfailed
succeeded_linesinteger
—
total_chargedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
total_linesinteger
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
partner.package_not_visible
409
idempotency.conflict
422
internal.limit_exceeded
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="POST"
REQ_PATH="/v1/orders/bulk"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"external_ref":"batch-2026-001","items":[{"external_ref":"po-1001","package_code":"TKE-VN-30D-X3K9F","quantity":5}]}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "created_at": "2026-07-11T08:12:30Z",
    "external_ref": "batch-2026-001",
    "failed_lines": 1,
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "items": [
      {
        "charged": null,
        "error": {
          "code": "TKE-VN-30D-X3K9F",
          "message": "<message>"
        },
        "external_ref": "po-1001",
        "line_no": 1,
        "order_id": "018f2b00-0000-7000-8000-00000000009a",
        "package_code": "TKE-VN-30D-X3K9F",
        "quantity": 2,
        "status": "pending"
      }
    ],
    "mode": "live",
    "status": "pending",
    "succeeded_lines": 1,
    "total_charged": {
      "amount": 273000,
      "currency": "VND"
    },
    "total_lines": 1
  },
  "ok": true
}

요청 본문 예시

json
{
  "external_ref": "batch-2026-001",
  "items": [
    {
      "external_ref": "po-1001",
      "package_code": "TKE-VN-30D-X3K9F",
      "quantity": 5
    }
  ]
}
GET/v1/orders/bulk/{id}

일괄 주문 결과

scope orders:readget_orders_bulk__id_

필요한 권한 범위: orders:read.

제출한 일괄 주문의 줄별 결과를 다시 조회합니다. 원래 응답을 잃어도 본문을 재전송하지 않고 복구할 수 있습니다. 다른 조직 또는 다른 환경(실운영/테스트)의 배치는 404입니다.

경로 매개변수 (1)

idstring필수

POST /v1/orders/bulk가 반환한 배치 ID(UUID)입니다。

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수

일괄 주문 결과입니다. 부분 성공 방식으로 실패한 줄은 다른 줄을 막지 않고 done 줄만 차감합니다. LPA/QR은 포함하지 않으며 각 order_id로 GET /v1/orders/{id}에서 조회하세요.

created_atstring · date-time
—
external_refstring
null 허용
—
failed_linesinteger
—
idstring · uuid
—
itemsobject[]
—
chargedobject
null 허용
—
errorobject
null 허용

실제 domain.reason 오류 코드입니다. 예: wallet.insufficient_balance, esim.out_of_stock, order.package_not_allowed, wallet.velocity_exceeded.

codestring
—
messagestring
—
external_refstring
null 허용
—
line_nointeger

제출한 순서 그대로 1부터 시작합니다.

order_idstring · uuid
null 허용
—
package_codestring
—
quantityinteger
—
statusstring
—
pendingdonefailed
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
statusstring

pending은 중단되어 아직 실행되지 않은 행이 있다는 뜻입니다. 같은 Idempotency-Key로 다시 호출해 나머지를 완료하세요.

pendingcompletedpartialfailed
succeeded_linesinteger
—
total_chargedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
total_linesinteger
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
internal.not_found
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="GET"
REQ_PATH="/v1/orders/bulk/$ID"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "created_at": "2026-07-11T08:12:30Z",
    "external_ref": "batch-2026-001",
    "failed_lines": 1,
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "items": [
      {
        "charged": null,
        "error": {
          "code": "TKE-VN-30D-X3K9F",
          "message": "<message>"
        },
        "external_ref": "po-1001",
        "line_no": 1,
        "order_id": "018f2b00-0000-7000-8000-00000000009a",
        "package_code": "TKE-VN-30D-X3K9F",
        "quantity": 2,
        "status": "pending"
      }
    ],
    "mode": "live",
    "status": "pending",
    "succeeded_lines": 1,
    "total_charged": {
      "amount": 273000,
      "currency": "VND"
    },
    "total_lines": 1
  },
  "ok": true
}

eSIM

eSIM 기록, 사용량, 충전 및 취소

GET/v1/esims

eSIM 목록 조회(조회 기반 대사)

scope esims:readget_esims

필요한 권한 범위: esims:read.

쿼리 매개변수 (8)

page_sizeinteger선택기본값 50min 1max 200

페이지당 레코드 수입니다. 기본 50, 최대 200이며 더 큰 값을 보내면 오류 없이 200으로 제한됩니다.

statusstring선택
허용 값:pendingavailablereservedsolddeliveredin_useused_upexpiredsuspendedcancelledprovider_failed

esim_status로 필터링합니다. 허용 목록에 없는 값은 빈 목록 대신 internal.invalid_filter(400)를 반환합니다. cancelled에서 l을 빠뜨린 canceled 같은 오타를 해당 상태의 eSIM이 없다는 뜻으로 오해하지 않도록 합니다.

order_idstring · uuid선택

하나의 주문에 속한 eSIM만 조회합니다.

iccidstring선택

ICCID로 필터링합니다(문자열, 정확히 일치).

external_refstring선택

주문 시 보낸 external_ref와 정확히 일치하는 주문의 모든 eSIM입니다.

updated_sincestring · date-time선택

ISO-8601 UTC 형식입니다. updated_at >= updated_since인 레코드를 오름차순으로 반환하는 표준 조회 기반 대사 경로(§8.5)입니다. 웹훅을 놓쳐도 상태를 복원할 수 있습니다. 형식이 틀리면 internal.invalid_filter(400)입니다.

cursorstring선택

다음 페이지 커서로 이전 next_cursor를 그대로 보내세요. 잘못된 커서는 오류 없이 목록 처음부터 다시 읽습니다. 이는 인코딩 변경 시 호환성을 유지하기 위한 동작입니다. 첫 페이지 무한 반복을 막으려면 다음 커서가 보낸 값과 다른지 확인하세요.

includestring선택

쉼표로 추가 블록을 요청합니다. lpa는 install, qr는 상세 조회에서만 install.qr_code_base64를 추가합니다. 반환된 코드는 공개된 것으로 간주하여 해당 eSIM의 취소가 영구 차단됩니다(cancellable.allowed = false, reason = code_disclosed). 즉시 전달할 때만 요청하고 사전 주문에서는 생략해 취소 권리를 유지하세요. 알 수 없는 값은 internal.invalid_filter(400, field: "include")입니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
itemsobject[]
—
cancellableobject

eSIM 취소 가능 여부와 불가 사유입니다. POST /v1/esims/{order_item_id}/cancel 호출 전에 읽으세요. 답변이므로 항상 존재하고 null이 아닙니다.

allowedboolean
—
disclosed_atstring · date-time
null 허용

코드가 시스템 밖으로 공개된 시각(ISO-8601 UTC)입니다. 아무도 보지 않았으면 null이며, 고객이 수신하지 못했는데 취소 불가라고 주장할 때 확인할 기록입니다.

reasonstring
null 허용

취소 불가 사유: code_disclosed(전송 또는 표시됨), wrong_status(취소 불가 상태), expired(만료), not_supported(취소 기능 미선언). allowed = true이면 null입니다. esim.not_cancellable의 meta.reason과 같은 코드입니다.

code_disclosedwrong_statusexpirednot_supportednull
external_refstring
null 허용

주문 시 보낸 external_ref로 eSIM을 주문과 연결합니다. 참조 없이 주문했으면 null입니다.

iccidstring
null 허용
—
idstring · uuid

모든 /v1/esims/{id}/… 경로에 사용하는 eSIM ID이며 웹훅의 order_item_id와 같습니다. ICCID만 있으면 GET /v1/esims?iccid=…로 id를 조회하세요.

installobject
null 허용

이 eSIM 설치에 필요한 모든 정보입니다. null은 include=lpa로 요청하지 않았다는 뜻입니다. 아직 설치 코드가 없는 경우와는 다르며, 그 경우 블록은 존재하고 lpa: null입니다.

이 블록이 포함된 응답에는 항상 Cache-Control: no-store가 설정됩니다.

android_urlstring
null 허용
—
apnstring
null 허용

기기가 자동 구성하지 못할 때 입력할 APN입니다. 현재 버전에서는 항상 null이며 알 수 없다는 뜻이지 불필요하다는 뜻이 아닙니다.

ios_urlstring
null 허용
—
lpastring
null 허용

eSIM 활성화 코드이며 판매 가능한 비밀 정보입니다. 이 문자열만으로 QR 코드를 생성할 수 있습니다.

matching_idstring
null 허용
—
qr_code_base64string
null 허용

QR 코드의 PNG data-URI입니다. 상세 엔드포인트에서 include=qr을 요청한 경우에만 포함되며 목록에는 포함되지 않습니다(행당 약 11KB, 100행이면 약 1.1MB). 목록에서는 lpa로 QR 코드를 생성하세요.

smdp_addressstring
null 허용
—
msisdnstring
null 허용
—
order_idstring · uuid
—
package_codestring
null 허용

카탈로그의 요금제 코드로, eSIM을 카탈로그와 연결하는 키입니다. 원본 요금제가 카탈로그에서 제거된 경우에만 null입니다.

package_namestring

변경될 수 있는 사람이 읽는 표시 이름입니다. 대사 키로 사용하지 마세요.

planobject
null 허용

이 eSIM에 현재 적용된 요금제입니다.

current_codestring
—
expires_atstring · date-time
null 허용
—
started_atstring · date-time
null 허용
—
smdp_statusstring
null 허용
—
statusstring
—
updated_atstring · date-time
—
usageobject
null 허용

eSIM 데이터 사용량 통계입니다. 조회한 적이 없으면 updated_at = null입니다。

expires_atstring · date-time
null 허용
—
remaining_bytesinteger · int64
null 허용
—
sourcestring
null 허용
—
pollwebhook
total_bytesinteger · int64
null 허용
—
updated_atstring · date-time
null 허용
—
used_bytesinteger · int64
null 허용
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
next_cursorstring
null 허용

다음 페이지를 조회할 때 값을 그대로 전달하세요. null만이 종료 신호이며, page_size보다 적게 반환되어도 다음 페이지가 있을 수 있습니다.

page_sizeinteger
null 허용

서버가 이 페이지에 적용한 최대 레코드 수이며 상한이 반영된 값입니다. 실제 items 항목 수가 아닙니다.

totalinteger · int64
null 허용

필터에 맞는 레코드 수입니다. 집계 비용이 너무 높으면 null입니다. 마지막 페이지 판단에는 이 값이 아닌 next_cursor를 사용하세요.

warningsobject[]

목록에 대한 설명이며 특이사항이 없으면 비어 있습니다. 고정 코드: filtered_by_key_package_filters, filtered_by_provider_scope, no_price_for_org, price_filter_removed_rows, provider_disabled, capability_missing. 각각 키 제한, 조직 공급원 범위, 미설정 가격, 가격 필터, 공급원 비활성, 기능 미선언을 뜻합니다. count는 해당 행 수이며 수치화할 수 없으면 null입니다.

codestring
—
filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing
countinteger · int64
null 허용
—
messagestring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failedinternal.invalid_filter
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="GET"
REQ_PATH="/v1/esims"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "items": [
      {
        "cancellable": {
          "allowed": true,
          "disclosed_at": "2026-07-11T08:12:30Z",
          "reason": "code_disclosed"
        },
        "external_ref": "po-1001",
        "iccid": "8984012345678901234",
        "id": "018f2b00-0000-7000-8000-00000000009a",
        "install": {
          "android_url": "https://esimsetup.google.com/esim_qrcode_provisioning?data=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
          "apn": "<apn>",
          "ios_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
          "lpa": "LPA:1$smdp.example.com$MATCH-ABC123",
          "matching_id": "MATCH-ABC123",
          "qr_code_base64": "<PNG base64 — đã rút gọn>",
          "smdp_address": "smdp.example.com"
        },
        "msisdn": "+84900000000",
        "order_id": "018f2b00-0000-7000-8000-00000000009a",
        "package_code": "TKE-VN-30D-X3K9F",
        "package_name": "Tên gói hiển thị",
        "plan": {
          "current_code": "TKE-VN-30D-X3K9F",
          "expires_at": "2026-08-10T00:00:00Z",
          "started_at": "2026-07-11T08:12:30Z"
        },
        "smdp_status": "released",
        "status": "delivered",
        "updated_at": "2026-07-11T09:03:11Z",
        "usage": {
          "expires_at": "2026-08-10T00:00:00Z",
          "remaining_bytes": 5368709120,
          "source": "poll",
          "total_bytes": 5368709120,
          "updated_at": "2026-07-11T09:03:11Z",
          "used_bytes": 5368709120
        }
      }
    ],
    "mode": "live",
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNy0xMVQwODoxMjozMFoifQ",
    "page_size": 50,
    "total": 128,
    "warnings": []
  },
  "ok": true
}
GET/v1/esims/{id}

eSIM 한 건 조회

scope esims:readget_esims__id_

필요한 권한 범위: esims:read.

경로 매개변수 (1)

idstring필수

판매된 eSIM의 고정 ID입니다. 재사용될 수 있는 ICCID가 아닙니다.

쿼리 매개변수 (1)

includestring선택

쉼표로 추가 블록을 요청합니다. lpa는 install, qr는 상세 조회에서만 install.qr_code_base64를 추가합니다. 반환된 코드는 공개된 것으로 간주하여 해당 eSIM의 취소가 영구 차단됩니다(cancellable.allowed = false, reason = code_disclosed). 즉시 전달할 때만 요청하고 사전 주문에서는 생략해 취소 권리를 유지하세요. 알 수 없는 값은 internal.invalid_filter(400, field: "include")입니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
cancellableobject

eSIM 취소 가능 여부와 불가 사유입니다. POST /v1/esims/{order_item_id}/cancel 호출 전에 읽으세요. 답변이므로 항상 존재하고 null이 아닙니다.

allowedboolean
—
disclosed_atstring · date-time
null 허용

코드가 시스템 밖으로 공개된 시각(ISO-8601 UTC)입니다. 아무도 보지 않았으면 null이며, 고객이 수신하지 못했는데 취소 불가라고 주장할 때 확인할 기록입니다.

reasonstring
null 허용

취소 불가 사유: code_disclosed(전송 또는 표시됨), wrong_status(취소 불가 상태), expired(만료), not_supported(취소 기능 미선언). allowed = true이면 null입니다. esim.not_cancellable의 meta.reason과 같은 코드입니다.

code_disclosedwrong_statusexpirednot_supportednull
external_refstring
null 허용

주문 시 보낸 external_ref로 eSIM을 주문과 연결합니다. 참조 없이 주문했으면 null입니다.

iccidstring
null 허용
—
idstring · uuid

모든 /v1/esims/{id}/… 경로에 사용하는 eSIM ID이며 웹훅의 order_item_id와 같습니다. ICCID만 있으면 GET /v1/esims?iccid=…로 id를 조회하세요.

installobject
null 허용

이 eSIM 설치에 필요한 모든 정보입니다. null은 include=lpa로 요청하지 않았다는 뜻입니다. 아직 설치 코드가 없는 경우와는 다르며, 그 경우 블록은 존재하고 lpa: null입니다.

이 블록이 포함된 응답에는 항상 Cache-Control: no-store가 설정됩니다.

android_urlstring
null 허용
—
apnstring
null 허용

기기가 자동 구성하지 못할 때 입력할 APN입니다. 현재 버전에서는 항상 null이며 알 수 없다는 뜻이지 불필요하다는 뜻이 아닙니다.

ios_urlstring
null 허용
—
lpastring
null 허용

eSIM 활성화 코드이며 판매 가능한 비밀 정보입니다. 이 문자열만으로 QR 코드를 생성할 수 있습니다.

matching_idstring
null 허용
—
qr_code_base64string
null 허용

QR 코드의 PNG data-URI입니다. 상세 엔드포인트에서 include=qr을 요청한 경우에만 포함되며 목록에는 포함되지 않습니다(행당 약 11KB, 100행이면 약 1.1MB). 목록에서는 lpa로 QR 코드를 생성하세요.

smdp_addressstring
null 허용
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
msisdnstring
null 허용
—
order_idstring · uuid
—
package_codestring
null 허용

카탈로그의 요금제 코드로, eSIM을 카탈로그와 연결하는 키입니다. 원본 요금제가 카탈로그에서 제거된 경우에만 null입니다.

package_namestring

변경될 수 있는 사람이 읽는 표시 이름입니다. 대사 키로 사용하지 마세요.

planobject
null 허용

이 eSIM에 현재 적용된 요금제입니다.

current_codestring
—
expires_atstring · date-time
null 허용
—
started_atstring · date-time
null 허용
—
smdp_statusstring
null 허용
—
statusstring
—
updated_atstring · date-time
—
usageobject
null 허용

eSIM 데이터 사용량 통계입니다. 조회한 적이 없으면 updated_at = null입니다。

expires_atstring · date-time
null 허용
—
remaining_bytesinteger · int64
null 허용
—
sourcestring
null 허용
—
pollwebhook
total_bytesinteger · int64
null 허용
—
updated_atstring · date-time
null 허용
—
used_bytesinteger · int64
null 허용
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failedinternal.invalid_filter
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
internal.not_found
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="GET"
REQ_PATH="/v1/esims/$ID"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "cancellable": {
      "allowed": true,
      "disclosed_at": "2026-07-11T08:12:30Z",
      "reason": "code_disclosed"
    },
    "external_ref": "po-1001",
    "iccid": "8984012345678901234",
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "install": {
      "android_url": "https://esimsetup.google.com/esim_qrcode_provisioning?data=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
      "apn": "<apn>",
      "ios_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
      "lpa": "LPA:1$smdp.example.com$MATCH-ABC123",
      "matching_id": "MATCH-ABC123",
      "qr_code_base64": "<PNG base64 — đã rút gọn>",
      "smdp_address": "smdp.example.com"
    },
    "mode": "live",
    "msisdn": "+84900000000",
    "order_id": "018f2b00-0000-7000-8000-00000000009a",
    "package_code": "TKE-VN-30D-X3K9F",
    "package_name": "Tên gói hiển thị",
    "plan": {
      "current_code": "TKE-VN-30D-X3K9F",
      "expires_at": "2026-08-10T00:00:00Z",
      "started_at": "2026-07-11T08:12:30Z"
    },
    "smdp_status": "released",
    "status": "delivered",
    "updated_at": "2026-07-11T09:03:11Z",
    "usage": {
      "expires_at": "2026-08-10T00:00:00Z",
      "remaining_bytes": 5368709120,
      "source": "poll",
      "total_bytes": 5368709120,
      "updated_at": "2026-07-11T09:03:11Z",
      "used_bytes": 5368709120
    }
  },
  "ok": true
}
POST/v1/esims/{id}/cancel

미사용 eSIM 취소

scope esims:writeIdempotency-Key 필수post_esims__id__cancel

필요한 권한 범위: esims:write.

경로 매개변수 (1)

idstring필수

판매된 eSIM의 고정 ID입니다. 재사용될 수 있는 ICCID가 아닙니다.

헤더 (1)

Idempotency-Keystring필수

클라이언트 생성 문자열입니다. 같은 키와 본문은 한 번만 실행되며 재시도는 원래 결과를 반환합니다. api_key_id 단위(§11)이므로 다른 키는 별개 명령입니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
cancelled_atstring · date-time
—
esim_idstring · uuid
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
refund_suggestedboolean

true이면 환불 요청이 수동 승인 대기 중이며 자금은 아직 이동하지 않았습니다. refunded.amount = 0을 환불 없음으로 해석하지 마세요.

refundedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
statusstring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
409
esim.not_cancellableidempotency.conflict
422
capability.not_supported
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="POST"
REQ_PATH="/v1/esims/$ID/cancel"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Idempotency-Key: $(uuidgen)"

응답 예시

json
{
  "data": {
    "cancelled_at": "2026-07-11T08:12:30Z",
    "esim_id": "018f2b00-0000-7000-8000-000000000000",
    "mode": "live",
    "refund_suggested": true,
    "refunded": {
      "amount": 273000,
      "currency": "VND"
    },
    "status": "cancelled"
  },
  "ok": true
}
POST/v1/esims/{id}/change-plan

eSIM 충전

scope esims:writeIdempotency-Key 필수post_esims__id__change-plan

필요한 권한 범위: esims:write.

경로 매개변수 (1)

idstring필수

판매된 eSIM의 고정 ID입니다. 재사용될 수 있는 ICCID가 아닙니다.

헤더 (1)

Idempotency-Keystring필수

클라이언트 생성 문자열입니다. 같은 키와 본문은 한 번만 실행되며 재시도는 원래 결과를 반환합니다. api_key_id 단위(§11)이므로 다른 키는 별개 명령입니다.

요청 본문필수application/json

필드유형제약 조건설명
package_codestring
필수

자사 카탈로그의 요금제 코드인 public_sku입니다. 공급업체가 변경되어도 요금제별로 불변이므로 대사 키로 안전하게 사용할 수 있습니다. 예시는 형식만 맞고 실제로 작동하지 않으니 GET /v1/catalog/packages에서 실제 코드를 조회하세요.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수

eSIM 요금제 변경 결과입니다.

chargedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
current_planobject
—
codestring
—
expires_atstring · date-time
null 허용
—
started_atstring · date-time
null 허용
—
esim_idstring · uuid
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
order_idstring · uuid
—
previous_planobject
null 허용
—
cancelled_atstring · date-time
null 허용
—
codestring
—
remaining_bytes_forfeitedinteger · int64
null 허용
—
warningstring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
402
wallet.insufficient_balance
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
partner.package_not_visible
409
esim.topup_not_supportedesim.topup_provider_mismatchesim.not_eligibleidempotency.conflict
422
capability.not_supported
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="POST"
REQ_PATH="/v1/esims/$ID/change-plan"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"package_code":"TKE-VN-7D-P4M2Q"}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "charged": {
      "amount": 273000,
      "currency": "VND"
    },
    "current_plan": {
      "code": "TKE-VN-30D-X3K9F",
      "expires_at": "2026-08-10T00:00:00Z",
      "started_at": "2026-07-11T08:12:30Z"
    },
    "esim_id": "018f2b00-0000-7000-8000-000000000000",
    "mode": "live",
    "order_id": "018f2b00-0000-7000-8000-00000000009a",
    "previous_plan": {
      "cancelled_at": "2026-07-11T08:12:30Z",
      "code": "TKE-VN-30D-X3K9F",
      "remaining_bytes_forfeited": 2147483648
    },
    "warning": "Dung lượng còn lại của gói cũ KHÔNG được cộng dồn."
  },
  "ok": true
}

요청 본문 예시

json
{
  "package_code": "TKE-VN-7D-P4M2Q"
}
GET/v1/esims/{id}/plan-options

이 eSIM에 유효한 충전 패키지

scope esims:readget_esims__id__plan-options

필요한 권한 범위: esims:read.

이 eSIM에 유효한 충전 요금제만 반환합니다. 서버가 id로 공급업체를 확인하고 해당 공급업체의 요금제만 필터링합니다. /v1에는 provider_id가 노출되지 않으므로 서버가 대신 필터링하여 다른 공급업체 요금제를 잘못 선택할 수 없게 합니다. 목록에 없는 코드로 POST /v1/esims/{id}/change-plan을 호출하면 409 esim.topup_provider_mismatch입니다.

공급업체가 연결된 적 없는 eSIM(단순 가져오기 또는 원본 정보 없음)은 빈 목록(total: 0)을 반환하며 오류가 아닙니다.

경로 매개변수 (1)

idstring필수

판매된 eSIM의 고정 ID입니다. 재사용될 수 있는 ICCID가 아닙니다.

쿼리 매개변수 (2)

page_sizeinteger선택기본값 50min 1max 200

페이지당 레코드 수입니다. 기본 50, 최대 200이며 더 큰 값을 보내면 오류 없이 200으로 제한됩니다.

cursorstring선택

다음 페이지 커서로 이전 next_cursor를 그대로 보내세요. 잘못된 커서는 오류 없이 목록 처음부터 다시 읽습니다. 이는 인코딩 변경 시 호환성을 유지하기 위한 동작입니다. 첫 페이지 무한 반복을 막으려면 다음 커서가 보낸 값과 다른지 확인하세요.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수

현재 eSIM 공급업체로 미리 필터링한 충전 패키지입니다. 각 항목은 카탈로그와 같은 완전한 Package입니다. policy는 현재 공급업체의 정책이며 null은 미확인이지 아무것도 취소되지 않는다는 뜻이 아닙니다.

itemsobject[]
—
capabilitiesobject

공급업체 유형으로 추론하지 않고 패키지가 실제 선언한 판매 후 기능입니다. 미선언 패키지는 모든 값이 false이며 반드시 422 capability.not_supported가 될 작업을 약속하지 않습니다.

cancelobject
—
restrictionstring
null 허용

code_not_disclosed는 고객 전달, 대시보드 열람, ?include=lpa 등으로 아무도 코드를 보지 않았을 때만 취소할 수 있다는 뜻입니다. 실제 취소 조건이며 시스템이 알 수 없는 설치 여부를 의미하지 않습니다. 이전 unused_only 표기는 부정확했습니다.

code_not_disclosednull
supportedboolean
—
plan_changeobject
—
policyobject
null 허용

이 공급업체에서 새 요금제를 추가 충전하는 방식입니다. null은 충전할 수 없거나 공급업체 정책을 아직 확인하지 못했다는 뜻입니다. 추측해서는 안 됩니다. 모든 값이 false인 객체가 아무것도 취소되지 않음을 보장하는 것과는 의미가 다릅니다.

……

더 깊은 필드는 JSON 예시를 확인하세요.

supportedboolean
—
usage_checkobject
—
supportedboolean
—
codestring

자사 카탈로그의 요금제 코드인 public_sku입니다. 공급업체가 변경되어도 요금제별로 불변이므로 대사 키로 안전하게 사용할 수 있습니다. 예시는 형식만 맞고 실제로 작동하지 않으니 GET /v1/catalog/packages에서 실제 코드를 조회하세요.

coverageobject

패키지 사용 가능 지역입니다.

countriesstring[]

ISO-3166 alpha-2 코드입니다. 글로벌 요금제(200개국을 모두 나열하지 않음)와 지원 국가가 명시되지 않은 요금제는 빈 배열입니다.

country_countinteger

모든 데이터를 조회하고 배열을 직접 세지 않고도 다국가 요금제를 정렬하거나 필터링할 수 있도록 제공하는 지원 국가 수입니다.

groupstring

범위: 국가 목록(country), 지역(region), 전 세계(global). 열린 열거형이므로 알 수 없는 값을 무시해야 합니다.

regionstring
null 허용

항상 null입니다. 지역은 패키지가 아닌 국가에 연결되므로 값을 추론하지 않습니다. ?region= 필터는 계속 작동합니다.

dataobject

요금제 데이터 용량입니다. 이중 반올림을 피하도록 모든 수치는 MB/GB가 아닌 바이트 단위입니다.

daily_bytesinteger · int64
null 허용

바이트 단위의 일일 데이터 용량입니다. fixed/unlimited 요금제는 null입니다.

labelstring

모든 화면에서 사용하는 동일한 함수로 생성한 표시 이름입니다.

modelstring

용량 계산 방식: daily(일일 용량), fixed(전체 기간 총량), unlimited입니다. 확장 가능한 열거형이므로 클라이언트는 알 수 없는 값을 무시해야 합니다.

dailyfixedunlimited
resetobject
null 허용

일일 데이터 초기화 시점입니다. 늦은 시간 구매 시 첫날 이용량에 영향을 주는 판매 정보입니다. null은 미선언입니다.

atstring
null 허용

timezone 기준 현지 초기화 시각입니다(00:00, 00:00:01).

notestring
null 허용
—
timezonestring
null 허용

초기화 시점의 시간대입니다(UTC+8, UTC+7).

total_bytesinteger · int64
null 허용

전체 기간 데이터 용량(바이트)입니다. daily/unlimited는 null입니다. 일일 용량에 일수를 곱한 값은 공급업체가 보장한 총량이 아닙니다.

descriptionstring
null 허용

패키지 판매 설명입니다. 작성되지 않았으면 null입니다.

description_enstring
null 허용

영문 설명입니다. 제공되지 않으면 null입니다.

featuresobject

패키지 기능을 코드 배열 대신 타입이 있는 객체로 표시합니다. []로는 핫스팟 미지원과 미확인을 구분할 수 없지만 false와 null은 구분할 수 있습니다.

appsobject[]

운영체제별 앱 지원 상태입니다.

배열에 없는 앱은 정보가 없다는 뜻이며, 지원한다는 뜻이 아닙니다. 목록에 있는 항목만 공급업체가 명시한 정보이고 현재는 TikTok만 명시되어 있습니다.

통합 필드는 의도적으로 제공하지 않습니다. 실제 카탈로그에서 2,465개 요금제가 iPhone의 TikTok을 지원하지만 Android에서는 지원하지 않습니다. 하나의 통합 플래그로 모두 미지원 처리하면 카탈로그의 약 3분의 1을 잘못 안내하게 됩니다.

androidstring

supported(공급업체 지원 명시), blocked(미지원 명시), unknown(해당 OS 정보 없음). 열린 열거형입니다.

supportedblockedunknown
appstring

소문자 앱 코드입니다(예: tiktok).

iosstring

supported(공급업체 지원 명시), blocked(미지원 명시), unknown(해당 OS 정보 없음). 열린 열거형입니다.

supportedblockedunknown
notestring
null 허용
—
hotspotboolean
null 허용

다른 기기와 Wi-Fi 공유 기능입니다. null은 미선언이며 false와 다릅니다.

kyc_requiredboolean
null 허용

사용 전에 신원 확인(KYC)이 필요합니다. null은 미명시를 뜻합니다.

smsboolean
null 허용

SMS입니다. false이면 문자 수신을 지원하지 않아 은행 OTP도 받을 수 없습니다. null은 미명시입니다.

voiceboolean
null 허용

음성 통화입니다. false는 공급업체가 미지원을 명시한 것이며 데이터 전용 회선은 전화를 받을 수 없습니다. null은 미선언입니다.

fupobject
null 허용

고속 데이터 소진 후 동작입니다. null은 속도 제한 정책 미선언입니다.

behaviorstring
null 허용

throttle(속도 제한), cutoff(연결 종료), null(미선언). 열린 열거형입니다.

throttlecutoffnull
high_speed_bytesinteger · int64
null 허용

속도 제한 전 고속 데이터 바이트 수입니다.

notestring
null 허용
—
speed_kbpsinteger
null 허용

고속 용량을 소진한 이후 속도(kbps)입니다. 연결이 끊기거나 정보가 미명시이면 null입니다. 전체 요금제 속도 상한인 network.speed_cap_kbps와 다릅니다.

marketstring

요금제의 공급 시스템으로 확장 가능한 열거형이므로 알 수 없는 값은 무시해야 합니다. 재고 유무와 판매 후 작업을 결정합니다. domestic은 키트를 판매하며 충전·취소·사용량 조회를 지원하고, international은 요청 시 공급업체에 주문하므로 재고가 없습니다.

domesticinternational
namestring
—
name_enstring
null 허용

영문 이름입니다. 제공되지 않으면 null입니다.

networkobject

패키지가 사용하는 네트워크입니다.

apnstring
null 허용

자동 설정이 되지 않는 일부 Android 기기에 수동 입력할 APN입니다. 설치 안내에 필요한 정보이며 없으면 고객 지원 문의로 이어집니다. null은 미명시입니다.

carriersobject[]

요금제가 사용하는 통신사입니다. 어느 무선망을 쓰는지에 대한 eSIM 사양이며 공급업체가 아닙니다.

namestring
—
generationsstring[]
null 허용

사용 가능한 이동통신 세대입니다(["4g", "5g"]). null은 미명시이며 2G 전용이라는 뜻이 아닙니다.

speed_cap_kbpsinteger
null 허용

전체 요금제의 속도 상한(kbps)으로, 고속 용량 소진 후 적용하는 fup.speed_kbps와 다릅니다.

priceobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
tagsstring[]

소문자와 하이픈으로 정규화한 상품 태그로, 키의 카탈로그 제한에도 사용됩니다. 현재 태그가 있는 요금제가 없으므로 판매자가 태그를 추가하기 전까지 ?tag=는 빈 목록을 반환합니다.

validityobject

요금제 유효기간과 시작 시점입니다.

daysinteger
—
install_by_daysinteger
null 허용

구매 후 QR 스캔 또는 eSIM 설치가 가능한 기한(일)입니다. null은 미명시이며 언제든 설치 가능하다는 뜻이 아닙니다.

labelstring
—
reclaim_on_expiryboolean
null 허용

만료 시 공급업체가 회선을 회수하여 충전과 요금제 변경이 불가능해집니다. 갱신 판매 시 기한을 알아야 합니다. null은 미선언입니다.

starts_whenstring
null 허용

기간 시작 시점: on_install(설치), on_purchase(구매), null. 만료일 계산에 중요한 정보이며 null이면 특정 만료일을 약속하지 마세요.

on_installon_purchasenull
unused_valid_daysinteger
null 허용

구매 후 사용하지 않고 보관할 수 있는 만료 전 일수입니다. null은 미명시이며 무기한이 아닙니다. 보관 유효기간은 설치 기한인 install_by_days와 별개입니다.

modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
next_cursorstring
null 허용

다음 페이지를 조회할 때 값을 그대로 전달하세요. null만이 종료 신호이며, page_size보다 적게 반환되어도 다음 페이지가 있을 수 있습니다.

page_sizeinteger

서버가 이 페이지에 적용한 최대 레코드 수이며 상한이 반영된 값입니다. 실제 items 항목 수가 아닙니다.

policyobject
null 허용

이 공급업체에서 새 요금제를 추가 충전하는 방식입니다. null은 충전할 수 없거나 공급업체 정책을 아직 확인하지 못했다는 뜻입니다. 추측해서는 안 됩니다. 모든 값이 false인 객체가 아무것도 취소되지 않음을 보장하는 것과는 의미가 다릅니다.

notestring
null 허용
—
requires_cancel_firstboolean

이 공급업체는 요금제를 누적하지 않으므로 새 요금제를 주문하기 전에 사용 중인 요금제를 취소합니다. 고객이 새 요금제 충전 버튼을 누르기 전에 안내하세요.

stackingboolean

새 요금제의 기간과 용량을 기존 요금제에 합산하는지 여부입니다.

totalinteger · int64
null 허용

필터에 맞는 총 패키지 수입니다. 국제 또는 market 미지정 등 계산 비용이 크면 null입니다. 키 제한과 조직 가격 검사 전에 계산하므로 구매 가능 수가 아닌 카탈로그 일치 수입니다.

warningsobject[]

목록에 대한 설명이며 특이사항이 없으면 비어 있습니다. 고정 코드: filtered_by_key_package_filters, filtered_by_provider_scope, no_price_for_org, price_filter_removed_rows, provider_disabled, capability_missing. 각각 키 제한, 조직 공급원 범위, 미설정 가격, 가격 필터, 공급원 비활성, 기능 미선언을 뜻합니다. count는 해당 행 수이며 수치화할 수 없으면 null입니다.

codestring
—
filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing
countinteger · int64
null 허용
—
messagestring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
internal.not_found
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="GET"
REQ_PATH="/v1/esims/$ID/plan-options"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "items": [
      {
        "capabilities": {
          "cancel": {
            "restriction": "code_not_disclosed",
            "supported": true
          },
          "plan_change": {
            "policy": {
              "note": "<note>",
              "requires_cancel_first": true,
              "stacking": true
            },
            "supported": true
          },
          "usage_check": {
            "supported": true
          }
        },
        "code": "TKE-VN-30D-X3K9F",
        "coverage": {
          "countries": [
            "CN"
          ],
          "country_count": 1,
          "group": "country",
          "region": "<region>"
        },
        "data": {
          "daily_bytes": 1073741824,
          "label": "1GB/ngày",
          "model": "daily",
          "reset": {
            "at": "00:00",
            "note": "<note>",
            "timezone": "UTC+8"
          },
          "total_bytes": null
        },
        "description": "<description>",
        "description_en": "<description_en>",
        "features": {
          "apps": [
            {
              "android": "blocked",
              "app": "tiktok",
              "ios": "supported",
              "note": "<note>"
            }
          ],
          "hotspot": true,
          "kyc_required": false,
          "sms": false,
          "voice": false
        },
        "fup": {
          "behavior": "throttle",
          "high_speed_bytes": 1048576,
          "note": "<note>",
          "speed_kbps": 128
        },
        "market": "international",
        "name": "eSIM Trung Quốc Mainland Multi - 1GB/ngày - 24 ngày",
        "name_en": "<name_en>",
        "network": {
          "apn": "e-ideas",
          "carriers": [
            {
              "name": "China Unicom"
            }
          ],
          "generations": [
            "4g",
            "5g"
          ],
          "speed_cap_kbps": 1
        },
        "price": {
          "amount": 273000,
          "currency": "VND"
        },
        "tags": [],
        "validity": {
          "days": 24,
          "install_by_days": 365,
          "label": "24 ngày",
          "reclaim_on_expiry": true,
          "starts_when": "on_install",
          "unused_valid_days": 1
        }
      }
    ],
    "mode": "live",
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNy0xMVQwODoxMjozMFoifQ",
    "page_size": 50,
    "policy": {
      "note": "<note>",
      "requires_cancel_first": true,
      "stacking": true
    },
    "total": 128,
    "warnings": []
  },
  "ok": true
}
GET/v1/esims/{id}/refund-requests

eSIM 환불 요청 기록(회원 전용)

scope esims:readget_esims__id__refund-requests

필요한 권한 범위: esims:read.

승인된 실제 회원 키만 가능합니다. 회원 소유 eSIM의 최근 환불 요청을 최신순으로 최대 50개 반환하며 next_cursor와 total은 null입니다. has_open_request는 검토 대기를 나타냅니다. 조직 및 테스트 키는 capability.not_supported입니다.

경로 매개변수 (1)

idstring필수

판매된 eSIM의 고정 ID입니다. 재사용될 수 있는 ICCID가 아닙니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
has_open_requestboolean
—
itemsobject[]
—
decided_atstring · date-time
null 허용
—
decision_notestring
null 허용
—
idstring · uuid
—
order_idstring · uuid
—
order_item_idstring · uuid
—
reasonstring
—
refundedobject
null 허용
—
requested_atstring · date-time
—
statusstring
—
pendingapprovedrejected
suggestedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
next_cursorstring
null 허용

다음 페이지를 조회할 때 값을 그대로 전달하세요. null만이 종료 신호이며, page_size보다 적게 반환되어도 다음 페이지가 있을 수 있습니다.

page_sizeinteger
null 허용

서버가 이 페이지에 적용한 최대 레코드 수이며 상한이 반영된 값입니다. 실제 items 항목 수가 아닙니다.

totalinteger · int64
null 허용

필터에 맞는 레코드 수입니다. 집계 비용이 너무 높으면 null입니다. 마지막 페이지 판단에는 이 값이 아닌 next_cursor를 사용하세요.

warningsobject[]

목록에 대한 설명이며 특이사항이 없으면 비어 있습니다. 고정 코드: filtered_by_key_package_filters, filtered_by_provider_scope, no_price_for_org, price_filter_removed_rows, provider_disabled, capability_missing. 각각 키 제한, 조직 공급원 범위, 미설정 가격, 가격 필터, 공급원 비활성, 기능 미선언을 뜻합니다. count는 해당 행 수이며 수치화할 수 없으면 null입니다.

codestring
—
filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing
countinteger · int64
null 허용
—
messagestring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
internal.not_found
422
capability.not_supported
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_member_live_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="GET"
REQ_PATH="/v1/esims/$ID/refund-requests"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "has_open_request": true,
    "items": [
      {
        "decided_at": "2026-07-11T08:12:30Z",
        "decision_note": "<decision_note>",
        "id": "018f2b00-0000-7000-8000-00000000009a",
        "order_id": "018f2b00-0000-7000-8000-00000000009a",
        "order_item_id": "018f2b00-0000-7000-8000-000000000000",
        "reason": "<reason>",
        "refunded": null,
        "requested_at": "2026-07-11T08:12:30Z",
        "status": "pending",
        "suggested": {
          "amount": 273000,
          "currency": "VND"
        }
      }
    ],
    "mode": "live",
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNy0xMVQwODoxMjozMFoifQ",
    "page_size": 50,
    "total": 128,
    "warnings": []
  },
  "ok": true
}
POST/v1/esims/{id}/refund-requests

eSIM 환불 요청 생성(회원 전용)

scope esims:writeIdempotency-Key 필수post_esims__id__refund-requests

필요한 권한 범위: esims:write.

승인된 실제 회원 키만 가능합니다. Idempotency-Key로 회원 eSIM의 환불을 신청하며 관리자 승인 전에는 자금이 이동하지 않습니다. 조직 및 테스트 키는 capability.not_supported입니다. 테스트 모드에서는 eSIM 취소로 가상 지갑 환불을 시험하세요.

경로 매개변수 (1)

idstring필수

판매된 eSIM의 고정 ID입니다. 재사용될 수 있는 ICCID가 아닙니다.

헤더 (1)

Idempotency-Keystring필수

클라이언트 생성 문자열입니다. 같은 키와 본문은 한 번만 실행되며 재시도는 원래 결과를 반환합니다. api_key_id 단위(§11)이므로 다른 키는 별개 명령입니다.

요청 본문필수application/json

필드유형제약 조건설명
reasonstring
필수

앞뒤 공백을 제거한 후 5~1000자의 환불 사유입니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
decided_atstring · date-time
null 허용
—
decision_notestring
null 허용
—
idstring · uuid
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
order_idstring · uuid
—
order_item_idstring · uuid
—
reasonstring
—
refundedobject
null 허용
—
requested_atstring · date-time
—
statusstring
—
pendingapprovedrejected
suggestedobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
internal.not_found
409
esim.refund_not_eligibleesim.refund_request_pendingidempotency.conflict
422
capability.not_supported
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_member_live_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="POST"
REQ_PATH="/v1/esims/$ID/refund-requests"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"reason":"<reason>"}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "decided_at": "2026-07-11T08:12:30Z",
    "decision_note": "<decision_note>",
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "mode": "live",
    "order_id": "018f2b00-0000-7000-8000-00000000009a",
    "order_item_id": "018f2b00-0000-7000-8000-000000000000",
    "reason": "<reason>",
    "refunded": null,
    "requested_at": "2026-07-11T08:12:30Z",
    "status": "pending",
    "suggested": {
      "amount": 273000,
      "currency": "VND"
    }
  },
  "ok": true
}

요청 본문 예시

json
{
  "reason": "<reason>"
}
GET/v1/esims/{id}/usage

사용한 데이터

scope esims:readget_esims__id__usage

필요한 권한 범위: esims:read.

eSIM의 사용량과 잔여 데이터를 값의 기준 시각 updated_at(실제 최신성)과 함께 조회합니다. 요청이 있을 때만 조회하는 공급사(현재 SkyFi)의 eSIM이면 이 호출은 스캔을 대기열에 넣고 결과를 최대 약 15초 기다리므로 응답에 최대 ~15초가 걸릴 수 있으며, 반환 값은 방금 끝난 스캔의 결과입니다. 스캔 실패, 같은 eSIM의 15초 간격 제한 이내, 또는 대기 시간 초과 시에는 오류를 반환하지 않고 마지막으로 저장된 값을 반환하므로 updated_at을 확인하세요. 다른 공급사는 주기적으로 갱신되는 저장 값을 반환합니다.

경로 매개변수 (1)

idstring필수

판매된 eSIM의 고정 ID입니다. 재사용될 수 있는 ICCID가 아닙니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
esim_idstring · uuid
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
usageobject
null 허용

eSIM 데이터 사용량 통계입니다. 조회한 적이 없으면 updated_at = null입니다。

expires_atstring · date-time
null 허용
—
remaining_bytesinteger · int64
null 허용
—
sourcestring
null 허용
—
pollwebhook
total_bytesinteger · int64
null 허용
—
updated_atstring · date-time
null 허용
—
used_bytesinteger · int64
null 허용
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
422
capability.not_supported
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="GET"
REQ_PATH="/v1/esims/$ID/usage"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "esim_id": "018f2b00-0000-7000-8000-000000000000",
    "mode": "live",
    "usage": {
      "expires_at": "2026-08-10T00:00:00Z",
      "remaining_bytes": 5368709120,
      "source": "poll",
      "total_bytes": 5368709120,
      "updated_at": "2026-07-11T09:03:11Z",
      "used_bytes": 5368709120
    }
  },
  "ok": true
}

지갑

지갑 잔액

GET/v1/wallet/balance

지갑 잔액

scope wallet:readget_wallet_balance

필요한 권한 범위: wallet:read.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
availableobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
balanceobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
credit_limitobject

금액입니다. /v1 전체에서 동일한 구조를 사용하며 객체와 숫자를 혼용하지 않습니다.

amountinteger · int64

currency의 최소 단위로 표시한 정수입니다(VND에는 소수 단위가 없음).

currencystring
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="GET"
REQ_PATH="/v1/wallet/balance"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "available": {
      "amount": 273000,
      "currency": "VND"
    },
    "balance": {
      "amount": 273000,
      "currency": "VND"
    },
    "credit_limit": {
      "amount": 273000,
      "currency": "VND"
    },
    "mode": "live"
  },
  "ok": true
}

웹훅

웹훅 설정, 전송 기록 및 시뮬레이터

POST/v1/testmode/webhooks/fail-next

다음 웹훅 실패 시뮬레이션(테스트 키 전용)

scope webhook:managepost_testmode_webhooks_fail-next

필요한 권한 범위: webhook:manage.

요청 본문필수application/json

필드유형제약 조건설명
countinteger
—

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
endpoint_idstring · uuid
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
testmode_fail_nextinteger
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbidden
404
internal.not_found
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="POST"
REQ_PATH="/v1/testmode/webhooks/fail-next"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"count":1}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "endpoint_id": "018f2b00-0000-7000-8000-000000000000",
    "mode": "live",
    "testmode_fail_next": 1
  },
  "ok": true
}

요청 본문 예시

json
{
  "count": 1
}
POST/v1/testmode/webhooks/fire

테스트 웹훅 이벤트 전송(테스트 키 전용)

scope webhook:managepost_testmode_webhooks_fire

필요한 권한 범위: webhook:manage.

요청 본문필수application/json

필드유형제약 조건설명
dataobject

샘플 페이로드의 data 필드를 부분적으로 덮어씁니다.

event_typestring
필수

발생시킬 웹훅 이벤트 유형입니다.

order.completedorder.failedesim.deliveredesim.activatedesim.usage_thresholdesim.expiringwallet.low_balancecatalog.updated

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
delivery_idstring · uuid
—
event_idstring · uuid
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbidden
404
webhook.not_configuredinternal.not_found
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="POST"
REQ_PATH="/v1/testmode/webhooks/fire"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"data":{},"event_type":"order.completed"}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "delivery_id": "018f2b00-0000-7000-8000-000000000000",
    "event_id": "018f3c00-0000-7000-8000-0000000000b0",
    "mode": "live"
  },
  "ok": true
}

요청 본문 예시

json
{
  "data": {},
  "event_type": "order.completed"
}
GET/v1/webhooks

웹훅 엔드포인트 목록 조회

scope webhook:manageget_webhooks

필요한 권한 범위: webhook:manage.

호출한 API 키의 웹훅 엔드포인트만 반환합니다. 같은 조직의 두 키라도 다른 키의 엔드포인트를 볼 수 없습니다(§5.11: api_key_id에 연결되며 테스트와 실운영은 공유하지 않음).

아직 설정하지 않았으면 404가 아닌 빈 배열과 200을 반환합니다. 컬렉션은 항상 존재하며 항목만 없을 수 있습니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
itemsobject[]
—
activeboolean

false이면 새 이벤트를 받지 않습니다. 대시보드에서 중지하거나 API 키를 폐기하면 같은 트랜잭션에서 채널이 종료됩니다. CHECK_HEALTH를 통과한 PUT /v1/webhooks/{id}로 다시 활성화합니다.

created_atstring · date-time
—
eventsstring[]

비어 있으면 모두 수신합니다.

idstring · uuid

/v1/webhooks/{id}에 넣는 엔드포인트 ID입니다. 생성할 때마다 새로 발급되므로 삭제 후 재생성은 기존 복구가 아닌 새 엔드포인트입니다.

last_delivery_atstring · date-time
null 허용

이 엔드포인트로 마지막 전송한 시각입니다. null은 전송 실패가 아닌 전송한 적 없음을 뜻하며, 파트너가 웹훅을 못 받았다고 할 때 먼저 확인할 값입니다.

secret_last4string

웹훅 비밀키의 마지막 네 글자입니다. 비밀키 전체를 다시 노출하지 않고 보유한 키가 서명에 쓰이는 키인지 확인합니다. 전체 비밀키는 생성 시 한 번만 표시됩니다.

urlstring
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
next_cursorstring
null 허용

다음 페이지를 조회할 때 값을 그대로 전달하세요. null만이 종료 신호이며, page_size보다 적게 반환되어도 다음 페이지가 있을 수 있습니다.

page_sizeinteger
null 허용

서버가 이 페이지에 적용한 최대 레코드 수이며 상한이 반영된 값입니다. 실제 items 항목 수가 아닙니다.

totalinteger · int64
null 허용

필터에 맞는 레코드 수입니다. 집계 비용이 너무 높으면 null입니다. 마지막 페이지 판단에는 이 값이 아닌 next_cursor를 사용하세요.

warningsobject[]

목록에 대한 설명이며 특이사항이 없으면 비어 있습니다. 고정 코드: filtered_by_key_package_filters, filtered_by_provider_scope, no_price_for_org, price_filter_removed_rows, provider_disabled, capability_missing. 각각 키 제한, 조직 공급원 범위, 미설정 가격, 가격 필터, 공급원 비활성, 기능 미선언을 뜻합니다. count는 해당 행 수이며 수치화할 수 없으면 null입니다.

codestring
—
filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing
countinteger · int64
null 허용
—
messagestring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="GET"
REQ_PATH="/v1/webhooks"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "items": [
      {
        "active": true,
        "created_at": "2026-07-11T08:12:30Z",
        "events": [
          "order.completed"
        ],
        "id": "018f2b00-0000-7000-8000-00000000009a",
        "last_delivery_at": "2026-07-11T08:12:30Z",
        "secret_last4": "a3f9",
        "url": "https://partner.example.com/hooks/tke"
      }
    ],
    "mode": "live",
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNy0xMVQwODoxMjozMFoifQ",
    "page_size": 50,
    "total": 128,
    "warnings": []
  },
  "ok": true
}
POST/v1/webhooks

웹훅 엔드포인트 생성

scope webhook:managepost_webhooks

필요한 권한 범위: webhook:manage.

호출한 키의 웹훅 엔드포인트를 생성합니다. 저장 전에 서버가 URL로 서명한 ping POST를 보냅니다(CHECK_HEALTH). 2xx가 아니면 저장하지 않고 422 webhook.health_check_failed입니다. 공개 https URL만 허용하며 내부/메타데이터 IP는 422 webhook.url_not_allowed입니다.

webhook_secret는 생성 시 한 번만 표시하며 다시 읽을 수 없습니다. 분실하면 대시보드에서 교체해야 하며 이전 비밀키도 24시간 함께 유효합니다(§8.4).

이미 엔드포인트가 있으면 일시 중지(is_active: false) 상태여도 409 webhook.already_configured입니다. 중지는 URL·비밀키·로그를 보존합니다. 새로 시작하려면 DELETE /v1/webhooks/{id} 후 생성하세요.

요청 본문필수application/json

필드유형제약 조건설명
eventsstring[]

비어 있으면 모두 수신합니다. 알 수 없는 값은 400 internal.validation_failed(필드 events)입니다.

urlstring · uri
필수
—

응답

201성공

성공

필드유형제약 조건설명
dataobject
필수
—
activeboolean

false이면 새 이벤트를 받지 않습니다. 대시보드에서 중지하거나 API 키를 폐기하면 같은 트랜잭션에서 채널이 종료됩니다. CHECK_HEALTH를 통과한 PUT /v1/webhooks/{id}로 다시 활성화합니다.

created_atstring · date-time
—
eventsstring[]

비어 있으면 모두 수신합니다.

health_checkstring
—
passed
idstring · uuid

/v1/webhooks/{id}에 넣는 엔드포인트 ID입니다. 생성할 때마다 새로 발급되므로 삭제 후 재생성은 기존 복구가 아닌 새 엔드포인트입니다.

last_delivery_atstring · date-time
null 허용

이 엔드포인트로 마지막 전송한 시각입니다. null은 전송 실패가 아닌 전송한 적 없음을 뜻하며, 파트너가 웹훅을 못 받았다고 할 때 먼저 확인할 값입니다.

modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
secret_last4string

웹훅 비밀키의 마지막 네 글자입니다. 비밀키 전체를 다시 노출하지 않고 보유한 키가 서명에 쓰이는 키인지 확인합니다. 전체 비밀키는 생성 시 한 번만 표시됩니다.

urlstring
—
webhook_secretstring

생성 시 한 번만 표시합니다(접두사 whsec_).

okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
409
webhook.already_configured
422
webhook.health_check_failedwebhook.url_not_allowed
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="POST"
REQ_PATH="/v1/webhooks"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"events":["order.completed"],"url":"https://partner.example.com/hooks/tke"}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "active": true,
    "created_at": "2026-07-11T08:12:30Z",
    "events": [
      "order.completed"
    ],
    "health_check": "passed",
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "last_delivery_at": "2026-07-11T08:12:30Z",
    "mode": "live",
    "secret_last4": "a3f9",
    "url": "https://partner.example.com/hooks/tke",
    "webhook_secret": "<webhook secret — chỉ hiện đúng một lần>"
  },
  "ok": true
}

요청 본문 예시

json
{
  "events": [
    "order.completed"
  ],
  "url": "https://partner.example.com/hooks/tke"
}
GET/v1/webhooks/{id}

웹훅 엔드포인트 한 건 조회

scope webhook:manageget_webhooks__id_

필요한 권한 범위: webhook:manage.

경로 매개변수 (1)

idstring필수

웹훅 엔드포인트 ID입니다(POST /v1/webhooks 또는 GET /v1/webhooks의 id).

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
activeboolean

false이면 새 이벤트를 받지 않습니다. 대시보드에서 중지하거나 API 키를 폐기하면 같은 트랜잭션에서 채널이 종료됩니다. CHECK_HEALTH를 통과한 PUT /v1/webhooks/{id}로 다시 활성화합니다.

created_atstring · date-time
—
eventsstring[]

비어 있으면 모두 수신합니다.

idstring · uuid

/v1/webhooks/{id}에 넣는 엔드포인트 ID입니다. 생성할 때마다 새로 발급되므로 삭제 후 재생성은 기존 복구가 아닌 새 엔드포인트입니다.

last_delivery_atstring · date-time
null 허용

이 엔드포인트로 마지막 전송한 시각입니다. null은 전송 실패가 아닌 전송한 적 없음을 뜻하며, 파트너가 웹훅을 못 받았다고 할 때 먼저 확인할 값입니다.

modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
secret_last4string

웹훅 비밀키의 마지막 네 글자입니다. 비밀키 전체를 다시 노출하지 않고 보유한 키가 서명에 쓰이는 키인지 확인합니다. 전체 비밀키는 생성 시 한 번만 표시됩니다.

urlstring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
webhook.not_found
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="GET"
REQ_PATH="/v1/webhooks/$ID"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "active": true,
    "created_at": "2026-07-11T08:12:30Z",
    "events": [
      "order.completed"
    ],
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "last_delivery_at": "2026-07-11T08:12:30Z",
    "mode": "live",
    "secret_last4": "a3f9",
    "url": "https://partner.example.com/hooks/tke"
  },
  "ok": true
}
PUT/v1/webhooks/{id}

웹훅 엔드포인트 수정

scope webhook:manageput_webhooks__id_

필요한 권한 범위: webhook:manage.

경로 매개변수 (1)

idstring필수

웹훅 엔드포인트 ID입니다(POST /v1/webhooks 또는 GET /v1/webhooks의 id).

요청 본문필수application/json

필드유형제약 조건설명
eventsstring[]

비어 있으면 모두 수신합니다. 알 수 없는 값은 400 internal.validation_failed(필드 events)입니다.

urlstring · uri
필수
—

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
activeboolean

false이면 새 이벤트를 받지 않습니다. 대시보드에서 중지하거나 API 키를 폐기하면 같은 트랜잭션에서 채널이 종료됩니다. CHECK_HEALTH를 통과한 PUT /v1/webhooks/{id}로 다시 활성화합니다.

created_atstring · date-time
—
eventsstring[]

비어 있으면 모두 수신합니다.

idstring · uuid

/v1/webhooks/{id}에 넣는 엔드포인트 ID입니다. 생성할 때마다 새로 발급되므로 삭제 후 재생성은 기존 복구가 아닌 새 엔드포인트입니다.

last_delivery_atstring · date-time
null 허용

이 엔드포인트로 마지막 전송한 시각입니다. null은 전송 실패가 아닌 전송한 적 없음을 뜻하며, 파트너가 웹훅을 못 받았다고 할 때 먼저 확인할 값입니다.

modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
secret_last4string

웹훅 비밀키의 마지막 네 글자입니다. 비밀키 전체를 다시 노출하지 않고 보유한 키가 서명에 쓰이는 키인지 확인합니다. 전체 비밀키는 생성 시 한 번만 표시됩니다.

urlstring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
webhook.not_found
422
webhook.health_check_failedwebhook.url_not_allowed
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="PUT"
REQ_PATH="/v1/webhooks/$ID"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"events":["order.completed"],"url":"https://partner.example.com/hooks/tke"}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "active": true,
    "created_at": "2026-07-11T08:12:30Z",
    "events": [
      "order.completed"
    ],
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "last_delivery_at": "2026-07-11T08:12:30Z",
    "mode": "live",
    "secret_last4": "a3f9",
    "url": "https://partner.example.com/hooks/tke"
  },
  "ok": true
}

요청 본문 예시

json
{
  "events": [
    "order.completed"
  ],
  "url": "https://partner.example.com/hooks/tke"
}
DELETE/v1/webhooks/{id}

웹훅 엔드포인트 제거(새 이벤트 수신 중지)

scope webhook:managedelete_webhooks__id_

필요한 권한 범위: webhook:manage.

엔드포인트를 제거하면 호출 키의 새 이벤트 수신이 중지됩니다.

webhook_secret도 함께 사라집니다. POST /v1/webhooks로 재생성하면 새 id와 새 비밀키가 발급되므로 검증 함수를 수정해야 합니다. 비밀키를 유지하는 일시 중지는 대시보드의 중지 버튼을 사용하세요.

전체 전송 로그는 대사용으로 유지되지만 삭제한 엔드포인트의 전송은 재시도할 수 없습니다.

재시도 대기 중인 pending 전송은 endpoint_disabled로 중지되고 로그에는 남습니다(§8.5).

다른 키의 id 또는 이미 삭제한 id는 조용히 200을 반환하지 않고 404 webhook.not_found입니다.

경로 매개변수 (1)

idstring필수

웹훅 엔드포인트 ID입니다(POST /v1/webhooks 또는 GET /v1/webhooks의 id).

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
deleted_atstring · date-time
—
idstring · uuid
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
webhook.not_found
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="DELETE"
REQ_PATH="/v1/webhooks/$ID"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "deleted_at": "2026-07-11T08:12:30Z",
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "mode": "live"
  },
  "ok": true
}
GET/v1/webhooks/deliveries

웹훅 전송 기록

scope webhook:manageget_webhooks_deliveries

필요한 권한 범위: webhook:manage.

쿼리 매개변수 (5)

pageinteger선택기본값 1min 1

1부터 시작하는 페이지 번호입니다. 마지막 페이지를 넘으면 items: []와 실제 total을 반환합니다(200, 404 아님).

page_sizeinteger선택기본값 50min 1max 200

페이지당 레코드 수입니다. 기본 50, 최대 200이며 더 큰 값을 보내면 오류 없이 200으로 제한됩니다.

event_typestring선택

이벤트 유형으로 필터링합니다(웹훅 이벤트 목록 참조).

statusstring선택
허용 값:pendingdeliveredfailed

전송 상태로 필터링합니다. ?status=failed는 조회 기반 웹훅 대사(§13)에 사용합니다. 알 수 없는 값은 오류 없이 무시하며 전체 로그를 반환합니다.

event_idstring · uuid선택

한 이벤트의 모든 전송 시도이며 재전송도 포함합니다.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
itemsobject[]
—
attemptsinteger
—
created_atstring · date-time
—
event_idstring · uuid

이벤트 식별자입니다. 재전송은 같은 event_id를 가진 새 전송을 생성하므로 전송 id가 아닌 event_id로 중복을 제거하세요.

event_typestring
—
idstring · uuid
—
last_attempt_atstring · date-time
null 허용
—
next_retry_atstring · date-time
null 허용
—
response_codeinteger
null 허용
—
statusstring
—
pendingdeliveredfailed
webhook_idstring · uuid
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
next_cursorstring
null 허용

다음 페이지를 조회할 때 값을 그대로 전달하세요. null만이 종료 신호이며, page_size보다 적게 반환되어도 다음 페이지가 있을 수 있습니다.

page_sizeinteger
null 허용

서버가 이 페이지에 적용한 최대 레코드 수이며 상한이 반영된 값입니다. 실제 items 항목 수가 아닙니다.

totalinteger · int64
null 허용

필터에 맞는 레코드 수입니다. 집계 비용이 너무 높으면 null입니다. 마지막 페이지 판단에는 이 값이 아닌 next_cursor를 사용하세요.

warningsobject[]

목록에 대한 설명이며 특이사항이 없으면 비어 있습니다. 고정 코드: filtered_by_key_package_filters, filtered_by_provider_scope, no_price_for_org, price_filter_removed_rows, provider_disabled, capability_missing. 각각 키 제한, 조직 공급원 범위, 미설정 가격, 가격 필터, 공급원 비활성, 기능 미선언을 뜻합니다. count는 해당 행 수이며 수치화할 수 없으면 null입니다.

codestring
—
filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing
countinteger · int64
null 허용
—
messagestring
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
webhook.not_configured
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="GET"
REQ_PATH="/v1/webhooks/deliveries"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "items": [
      {
        "attempts": 3,
        "created_at": "2026-07-11T08:12:30Z",
        "event_id": "018f3c00-0000-7000-8000-0000000000b0",
        "event_type": "order.completed",
        "id": "018f2b00-0000-7000-8000-00000000009a",
        "last_attempt_at": "2026-07-11T09:05:00Z",
        "next_retry_at": "2026-07-11T09:10:00Z",
        "response_code": 200,
        "status": "pending",
        "webhook_id": "018f2b00-0000-7000-8000-000000000000"
      }
    ],
    "mode": "live",
    "next_cursor": "eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNy0xMVQwODoxMjozMFoifQ",
    "page_size": 50,
    "total": 128,
    "warnings": []
  },
  "ok": true
}
POST/v1/webhooks/deliveries/{id}/replay

웹훅 재전송

scope webhook:managepost_webhooks_deliveries__id__replay

필요한 권한 범위: webhook:manage.

경로 매개변수 (1)

idstring필수

웹훅 전송 한 건의 ID입니다(GET /v1/webhooks/deliveries의 items[].id).

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
attemptsinteger
—
created_atstring · date-time
—
event_idstring · uuid

이벤트 식별자입니다. 재전송은 같은 event_id를 가진 새 전송을 생성하므로 전송 id가 아닌 event_id로 중복을 제거하세요.

event_typestring
—
idstring · uuid
—
last_attempt_atstring · date-time
null 허용
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
next_retry_atstring · date-time
null 허용
—
response_codeinteger
null 허용
—
statusstring
—
pendingdeliveredfailed
webhook_idstring · uuid
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspended
404
webhook.delivery_not_foundwebhook.not_configured
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="POST"
REQ_PATH="/v1/webhooks/deliveries/$ID/replay"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "attempts": 3,
    "created_at": "2026-07-11T08:12:30Z",
    "event_id": "018f3c00-0000-7000-8000-0000000000b0",
    "event_type": "order.completed",
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "last_attempt_at": "2026-07-11T09:05:00Z",
    "mode": "live",
    "next_retry_at": "2026-07-11T09:10:00Z",
    "response_code": 200,
    "status": "pending",
    "webhook_id": "018f2b00-0000-7000-8000-000000000000"
  },
  "ok": true
}

테스트 모드

api-testing 호스트에서 tke_test_ 또는 tke_member_test_ 테스트 키만 사용하세요. 실제 잔액이나 공급자 없이 소유자의 테스트 eSIM과 데이터를 모의합니다. 회원 테스트 모드는 웹훅, 대량 주문, 검토용 환불 요청을 지원하지 않습니다.

POST/v1/testmode/esims/{id}/simulate

테스트 eSIM 수명주기 시뮬레이션(테스트 키 전용)

scope esims:writepost_testmode_esims__id__simulate

필요한 권한 범위: esims:write.

tke_test_ 또는 tke_member_test_ 테스트 키 전용입니다. 소유자의 테스트 eSIM에 사용량, 설치 상태, 만료, 속도 제한 또는 다음 작업의 공급자 거부를 모의합니다. 본문은 최소 한 필드가 필요하며 비어 있으면 422 testmode.invalid_scenario를 반환합니다. 실제 모드와 같이 GET /v1/esims/{id}와 /usage로 결과를 읽습니다. 요금제 변경 및 취소 정책은 같으며 실제 공급자를 호출하지 않습니다.

경로 매개변수 (1)

idstring필수

판매된 eSIM의 고정 ID입니다. 재사용될 수 있는 ICCID가 아닙니다.

요청 본문필수application/json

필드유형제약 조건설명
data_used_mbinteger · int64

이미 사용한 데이터(MB, 이진 1 MiB 단위)입니다. 허용량보다 크면 허용량으로 제한하며 이는 데이터 소진 시나리오이지 오류가 아닙니다.

expire_atstring · date-time

만료 시각을 지정합니다(ISO-8601 UTC).

expire_nowboolean

true이면 요금제를 즉시 만료시킵니다(esim_status가 expired로 변경).

provider_rejectboolean

다음 작업(충전/취소)에서 공급업체 거절을 시뮬레이션합니다.

statusstring

공급업체 용어로 표현한 eSIM 프로필의 기술적 수명주기입니다. eSIM 조회가 반환하는 비즈니스 상태 esim_status와 다릅니다.

releasedinstalledenableddisabled
throttledboolean

true이면 회선이 제한 속도 상태입니다(무제한 패키지의 고속 데이터 한도 초과).

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
cancellableobject

eSIM 취소 가능 여부와 불가 사유입니다. POST /v1/esims/{order_item_id}/cancel 호출 전에 읽으세요. 답변이므로 항상 존재하고 null이 아닙니다.

allowedboolean
—
disclosed_atstring · date-time
null 허용

코드가 시스템 밖으로 공개된 시각(ISO-8601 UTC)입니다. 아무도 보지 않았으면 null이며, 고객이 수신하지 못했는데 취소 불가라고 주장할 때 확인할 기록입니다.

reasonstring
null 허용

취소 불가 사유: code_disclosed(전송 또는 표시됨), wrong_status(취소 불가 상태), expired(만료), not_supported(취소 기능 미선언). allowed = true이면 null입니다. esim.not_cancellable의 meta.reason과 같은 코드입니다.

code_disclosedwrong_statusexpirednot_supportednull
external_refstring
null 허용

주문 시 보낸 external_ref로 eSIM을 주문과 연결합니다. 참조 없이 주문했으면 null입니다.

iccidstring
null 허용
—
idstring · uuid

모든 /v1/esims/{id}/… 경로에 사용하는 eSIM ID이며 웹훅의 order_item_id와 같습니다. ICCID만 있으면 GET /v1/esims?iccid=…로 id를 조회하세요.

installobject
null 허용

이 eSIM 설치에 필요한 모든 정보입니다. null은 include=lpa로 요청하지 않았다는 뜻입니다. 아직 설치 코드가 없는 경우와는 다르며, 그 경우 블록은 존재하고 lpa: null입니다.

이 블록이 포함된 응답에는 항상 Cache-Control: no-store가 설정됩니다.

android_urlstring
null 허용
—
apnstring
null 허용

기기가 자동 구성하지 못할 때 입력할 APN입니다. 현재 버전에서는 항상 null이며 알 수 없다는 뜻이지 불필요하다는 뜻이 아닙니다.

ios_urlstring
null 허용
—
lpastring
null 허용

eSIM 활성화 코드이며 판매 가능한 비밀 정보입니다. 이 문자열만으로 QR 코드를 생성할 수 있습니다.

matching_idstring
null 허용
—
qr_code_base64string
null 허용

QR 코드의 PNG data-URI입니다. 상세 엔드포인트에서 include=qr을 요청한 경우에만 포함되며 목록에는 포함되지 않습니다(행당 약 11KB, 100행이면 약 1.1MB). 목록에서는 lpa로 QR 코드를 생성하세요.

smdp_addressstring
null 허용
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
msisdnstring
null 허용
—
order_idstring · uuid
—
package_codestring
null 허용

카탈로그의 요금제 코드로, eSIM을 카탈로그와 연결하는 키입니다. 원본 요금제가 카탈로그에서 제거된 경우에만 null입니다.

package_namestring

변경될 수 있는 사람이 읽는 표시 이름입니다. 대사 키로 사용하지 마세요.

planobject
null 허용

이 eSIM에 현재 적용된 요금제입니다.

current_codestring
—
expires_atstring · date-time
null 허용
—
started_atstring · date-time
null 허용
—
simulatedobject

시나리오 적용 후의 시뮬레이션 상태입니다.

esim_statestring

테스트 eSIM의 기술적 수명주기입니다. 실운영에서는 smdp_status에서 읽지만 테스트 eSIM에는 SM-DP+ 프로필이 없으므로 다른 테스트 전용 필드와 함께 제공합니다.

releasedinstalledenableddisabled
last_viastring

가장 최근 이 eSIM을 변경한 흐름입니다.

open_apisimulateseed
viastring

이 테스트 eSIM을 생성한 흐름입니다.

open_apisimulateseed
smdp_statusstring
null 허용
—
statusstring
—
updated_atstring · date-time
—
usageobject
null 허용

eSIM 데이터 사용량 통계입니다. 조회한 적이 없으면 updated_at = null입니다。

expires_atstring · date-time
null 허용
—
remaining_bytesinteger · int64
null 허용
—
sourcestring
null 허용
—
pollwebhook
total_bytesinteger · int64
null 허용
—
updated_atstring · date-time
null 허용
—
used_bytesinteger · int64
null 허용
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbidden
404
internal.not_found
422
testmode.invalid_scenario
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"
ID="THAY-BANG-ID"

METHOD="POST"
REQ_PATH="/v1/testmode/esims/$ID/simulate"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"data_used_mb":4096,"expire_at":"2026-07-11T08:12:30Z","expire_now":true,"provider_reject":true,"status":"enabled","throttled":true}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "cancellable": {
      "allowed": true,
      "disclosed_at": "2026-07-11T08:12:30Z",
      "reason": "code_disclosed"
    },
    "external_ref": "po-1001",
    "iccid": "8984012345678901234",
    "id": "018f2b00-0000-7000-8000-00000000009a",
    "install": {
      "android_url": "https://esimsetup.google.com/esim_qrcode_provisioning?data=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
      "apn": "<apn>",
      "ios_url": "https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA%3A1%24smdp.example.com%24MATCH-ABC123",
      "lpa": "LPA:1$smdp.example.com$MATCH-ABC123",
      "matching_id": "MATCH-ABC123",
      "qr_code_base64": "<PNG base64 — đã rút gọn>",
      "smdp_address": "smdp.example.com"
    },
    "mode": "live",
    "msisdn": "+84900000000",
    "order_id": "018f2b00-0000-7000-8000-00000000009a",
    "package_code": "TKE-VN-30D-X3K9F",
    "package_name": "Tên gói hiển thị",
    "plan": {
      "current_code": "TKE-VN-30D-X3K9F",
      "expires_at": "2026-08-10T00:00:00Z",
      "started_at": "2026-07-11T08:12:30Z"
    },
    "simulated": {
      "esim_state": "released",
      "last_via": "open_api",
      "via": "open_api"
    },
    "smdp_status": "released",
    "status": "delivered",
    "updated_at": "2026-07-11T09:03:11Z",
    "usage": {
      "expires_at": "2026-08-10T00:00:00Z",
      "remaining_bytes": 5368709120,
      "source": "poll",
      "total_bytes": 5368709120,
      "updated_at": "2026-07-11T09:03:11Z",
      "used_bytes": 5368709120
    }
  },
  "ok": true
}

요청 본문 예시

json
{
  "data_used_mb": 4096,
  "expire_at": "2026-07-11T08:12:30Z",
  "expire_now": true,
  "provider_reject": true,
  "status": "enabled",
  "throttled": true
}
POST/v1/testmode/reset

테스트 데이터 초기화(테스트 키 전용)

scope orders:writepost_testmode_reset

필요한 권한 범위: orders:write.

헤더 (1)

Idempotency-Keystring선택

요청 본문선택application/json

필드유형제약 조건설명
reasonstring
—

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
esim_states_deletedinteger · int64
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
order_items_deletedinteger · int64
—
orders_deletedinteger · int64
—
wallet_balanceinteger · int64

초기화 후 테스트 지갑 잔액이며 항상 0입니다. 원장은 삭제하지 않고 상계 분개로 잔액을 0으로 만듭니다.

okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbidden
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="POST"
REQ_PATH="/v1/testmode/reset"   # them query thi noi thang vao day TRUOC khi ky
BODY='{"reason":"Reset test data"}'

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"

응답 예시

json
{
  "data": {
    "esim_states_deleted": 1,
    "mode": "live",
    "order_items_deleted": 1,
    "orders_deleted": 1,
    "wallet_balance": 1
  },
  "ok": true
}

요청 본문 예시

json
{
  "reason": "Reset test data"
}
POST/v1/testmode/seed

테스트 모드 샘플 데이터 생성(테스트 키 전용)

scope orders:writepost_testmode_seed

필요한 권한 범위: orders:write.

헤더 (1)

Idempotency-Keystring선택

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
already_seededboolean
—
esims_createdinteger
—
modestring

호출 키의 환경이며 X-TKE-Mode 헤더와 일치합니다. 조직 키는 tke_live_/tke_test_, 회원 키는 tke_member_live_/tke_member_test_입니다. 실제 및 테스트 호스트는 키 모드를 검사합니다.

testlive
orders_createdinteger
—
seededboolean
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbidden
422
testmode.no_sellable_package
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="POST"
REQ_PATH="/v1/testmode/seed"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG" \
  -H "Idempotency-Key: $(uuidgen)"

응답 예시

json
{
  "data": {
    "already_seeded": true,
    "esims_created": 1,
    "mode": "live",
    "orders_created": 1,
    "seeded": true
  },
  "ok": true
}
GET/v1/testmode/status

테스트 모드 개요(테스트 키 전용)

scope esims:readget_testmode_status

필요한 권한 범위: esims:read.

응답

200성공

성공

필드유형제약 조건설명
dataobject
필수
—
base_url_testingstring

테스트 모드 호스트입니다. tke_test_ 또는 tke_member_test_ 키만 사용하세요. 실제 호스트 호출은 403 partner.wrong_host를 반환합니다.

capabilitiesobject
—
bulk_ordersboolean
—
cancel_refundboolean
—
refund_requestsboolean
—
webhooksboolean
—
checklistobject[]

동일 소유자의 테스트 모드 실제 API 기록으로 통합 단계를 계산합니다. 웹훅 단계는 조직에만 적용됩니다.

doneboolean
—
labelstring
—
last_atstring · date-time
null 허용
—
stepstring
—
driftobject

모의 사용량 증가 주기의 활성 상태, 간격, 최근 실행 시각입니다. 임계값 웹훅은 웹훅을 지원하는 조직에만 전송됩니다.

enabledboolean
—
interval_secsinteger · int64
—
last_run_atstring · date-time
null 허용
—
modestring
—
testlive
readyboolean

키 소유자가 지원되는 체크리스트 단계를 모두 완료했는지 나타냅니다. 회원은 웹훅 테스트가 필요하지 않습니다.

sample_countsobject
—
test_esimsinteger · int64
—
test_ordersinteger · int64
—
seededboolean

키 소유자에게 샘플 테스트 주문과 eSIM이 이미 있는지 나타냅니다.

test_key_countinteger · int64
—
wallet_balanceinteger · int64

키 소유자의 가상 VND 테스트 지갑 잔액이며 실제 지갑과 분리됩니다.

webhookobject
—
endpoint_urlstring
null 허용
—
fail_nextinteger
—
statusstring
null 허용
—
okboolean
필수
—
true

오류 응답

모든 오류는 다음 형식을 반환합니다: { ok: false, error } 적절한 HTTP 상태를 사용하며 200은 반환하지 않습니다. 오류 코드 페이지를 참조하세요.

400
internal.validation_failed
401
partner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reused
403
partner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbidden
429
partner.rate_limited
503
partner.endpoint_disabledinternal.service_unavailable

호출 예시

다음 모든 값 TKE-VN-30D-X3K9F 은 직접 입력할 자리입니다. 실제 요금제 코드는 다음으로 조회하세요: GET /v1/catalog/packages.

bash
#!/usr/bin/env bash
# Can: bash + openssl + uuidgen.

KEY_ID="tke_test_THAY-BANG-KEY-ID-CUA-BAN"
SECRET="THAY-BANG-SECRET-CUA-BAN"

METHOD="GET"
REQ_PATH="/v1/testmode/status"   # them query thi noi thang vao day TRUOC khi ky
BODY=""

TS="$(date +%s000)"                 # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"

curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
  -H "Accept-Language: ko" \
  -H "X-TKE-Key: $KEY_ID" \
  -H "X-TKE-Timestamp: $TS" \
  -H "X-TKE-Nonce: $NONCE" \
  -H "X-TKE-Signature: $SIG"

응답 예시

json
{
  "data": {
    "base_url_testing": "<base_url_testing>",
    "capabilities": {
      "bulk_orders": true,
      "cancel_refund": true,
      "refund_requests": true,
      "webhooks": true
    },
    "checklist": [
      {
        "done": true,
        "label": "<label>",
        "last_at": "2026-07-11T08:12:30Z",
        "step": "<step>"
      }
    ],
    "drift": {
      "enabled": true,
      "interval_secs": 1,
      "last_run_at": "2026-07-11T08:12:30Z"
    },
    "mode": "test",
    "ready": true,
    "sample_counts": {
      "test_esims": 1,
      "test_orders": 1
    },
    "seeded": true,
    "test_key_count": 1,
    "wallet_balance": 1,
    "webhook": {
      "endpoint_url": "https://partner.example.com/hooks/tke",
      "fail_next": 1,
      "status": "completed"
    }
  },
  "ok": true
}