Справочник API
Все методы /v1. Страница формируется из спецификации OpenAPI, созданной при сборке сервера: пути, параметры, схемы, перечисления и ошибки соответствуют поведению сервера.
Рабочий режим
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_ (партнёры / участники).
Общие правила API
Следующие правила (6) действуют для всех операций (30) и не повторяются в описаниях отдельных методов.
Каждый метод /v1, включая GET, требует подписи HMAC-SHA256 с 4 заголовками: X-TKE-Key · X-TKE-Timestamp · X-TKE-Nonce · X-TKE-Signature. Обхода аутентификации нет.
Одинаковые ключ и тело в течение 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.
Два вида пагинации
Используйте отдельную обработку: форматы ответов и условия завершения различаются.
Используется только в GET /v1/webhooks/deliveries. Возвращает page, page_size, total.
За пределами списка возвращаются items: [] и фактический total с HTTP 200, не 404. Остановитесь при пустом items или после обработки total элементов.
Используется в GET /v1/catalog/packages, GET /v1/orders, GET /v1/esims и GET /v1/esims/{id}/plan-options. Заказы и eSIM поддерживают сверку через updated_since; каталог и варианты тарифов — нет.
Остановитесь, если next_cursor ОТСУТСТВУЕТ или РАВЕН текущему курсору. Поле может полностью отсутствовать в data (тип string | null | undefined). Без этой проверки возможен бесконечный цикл.
Неверный курсор не вызывает ошибку: сервер возвращается к началу списка, сохраняя совместимость клиентов при смене кодирования. Поэтому цикл с проверкой только next_cursor !== null может бесконечно читать первую страницу.
let cursor = null;
const seen = new Set();
while (true) {
const params = new URLSearchParams({ page_size: "200" });
if (cursor) params.set("cursor", cursor);
// callSigned — ваша функция, добавляющая 4 заголовков X-TKE-* (см. страницу 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`.
/v1/catalog/packagesСписок тарифов
catalog:readget_catalog_packagesТребуемая область доступа: catalog:read.
Каталог организации: ЕДИНЫЙ список для двух систем поставки с фильтром ?market=. Возвращаются только реально доступные для ПОКУПКИ тарифы: опубликованные, поставляемые, разрешённые ключу и с ценой организации. Курсорная пагинация: первый запрос без cursor, затем передавайте next_cursor до null. Короткая страница НЕ означает конец. 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необязательноtruefalsetrue — только тарифы с ЗАЯВЛЕННОЙ раздачей Wi-Fi; false — с ЗАЯВЛЕННЫМ запретом. Без декларации (features.hotspot = null) тариф не проходит НИ ОДИН вариант. Допустимы только строки true/false.
appstringнеобязательноТолько тарифы с ЗАЯВЛЕННОЙ поддержкой приложения хотя бы на одной ОС (app=tiktok). Отсутствие сведений не означает поддержку. Пока принимается только tiktok; другой код: internal.invalid_filter (400).
voicestringнеобязательноtruefalsetrue — только тарифы с ЗАЯВЛЕННЫМ голосом; false — с ЗАЯВЛЕННЫМ отсутствием. Без декларации тариф не проходит ни один вариант. Допустимы только строки true/false.
cursorstringнеобязательноПередайте next_cursor предыдущей страницы БЕЗ ИЗМЕНЕНИЙ. Остановитесь при next_cursor = null, а НЕ при короткой странице. Ограничения каталога ключа, наличие цены организации и фильтр цены применяются ПОСЛЕ разбиения: за страницей из 3 элементов могут оставаться тысячи тарифов. Курсор содержит ОТПЕЧАТОК фильтров. Другие фильтры или повреждённый курсор дают catalog.cursor_mismatch (400), чтобы не смешивать результаты разных запросов.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
items | object[] | — | |
capabilities | object | Доступные действия ПОСЛЕ продажи из РЕАЛЬНОЙ декларации тарифа, без предположений по типу поставщика. Без декларации все флаги | |
cancel | object | — | |
restriction | string | допускает null |
code_not_disclosednull |
supported | boolean | — | |
plan_change | object | — | |
policy | object | допускает null | Политика ДОБАВЛЕНИЯ НОВОГО ТАРИФА поставщика. |
… | … | Вложенные поля смотрите в примере JSON. | |
supported | boolean | — | |
usage_check | object | — | |
supported | boolean | — | |
code | string | Код тарифа ВАШЕГО каталога, | |
coverage | object | Где работает тариф. | |
countries | string[] | Коды ISO-3166 alpha-2. Пустой массив у глобальных тарифов (200 стран не перечисляются) и тарифов без заявленного покрытия. | |
country_count | integer | Количество стран покрытия для сортировки или фильтрации многострановых тарифов без загрузки и подсчёта всего массива. | |
group | string | Тип покрытия: страны | |
region | string | допускает null | Пока ВСЕГДА |
data | object | Лимит данных тарифа. ВСЕ значения в БАЙТАХ, не МБ/ГБ, чтобы избежать двойного округления. | |
daily_bytes | integer · int64 | допускает null | ДНЕВНОЙ лимит данных в байтах. |
label | string | Понятная пользователю подпись, созданная общей для всех экранов функцией. | |
model | string | Учёт лимита: dailyfixedunlimited | |
reset | object | допускает null | Момент сброса дневного лимита. Неизвестное время может лишить купившего в 22:00 почти всего первого дня. |
at | string | допускает null | Местное время сброса в часовом поясе |
note | string | допускает null | — |
timezone | string | допускает null | Часовой пояс сброса ( |
total_bytes | integer · int64 | допускает null | Общий лимит на весь срок в байтах. |
description | string | допускает null | Описание тарифа для продажи. |
description_en | string | допускает null | Описание на английском. |
features | object | Возможности тарифа. ТИПИЗИРОВАННЫЙ объект: массив | |
apps | object[] | Поддержка приложения по ОПЕРАЦИОННЫМ СИСТЕМАМ. Отсутствие приложения в массиве означает ОТСУТСТВИЕ ДАННЫХ, а не поддержку. Поставщик гарантирует только перечисленное; пока заявлен только TikTok. Объединённого флага нет намеренно: в реальном каталоге 2465 тарифов поддерживают TikTok на iPhone, но не Android. Единый флаг исказил бы почти треть каталога. | |
android | string |
supportedblockedunknown | |
app | string | Код приложения в нижнем регистре, например | |
ios | string |
supportedblockedunknown | |
note | string | допускает null | — |
hotspot | boolean | допускает null | Раздача Wi-Fi другим устройствам. |
kyc_required | boolean | допускает null | Перед использованием требуется проверка личности KYC. |
sms | boolean | допускает null | SMS. |
voice | boolean | допускает null | Голосовые вызовы. |
fup | object | допускает null | Поведение после исчерпания скоростного лимита. |
behavior | string | допускает null |
throttlecutoffnull |
high_speed_bytes | integer · int64 | допускает null | Объём в БАЙТАХ на полной скорости до ограничения. |
note | string | допускает null | — |
speed_kbps | integer | допускает null | Скорость ПОСЛЕ исчерпания скоростного лимита, кбит/с. |
market | string | СИСТЕМА ПОСТАВКИ, ОТКРЫТОЕ перечисление: игнорируйте неизвестные значения. Определяет запас и действия после продажи: domesticinternational | |
name | string | — | |
name_en | string | допускает null | Название на английском. |
network | object | Сети, в которых работает тариф. | |
apn | string | допускает null | APN для ручного ввода на Android, если устройство не подхватило его автоматически. Требуется для инструкции установки, иначе клиенту понадобится поддержка. |
carriers | object[] | Операторы связи, В СЕТЯХ которых работает тариф. Характеристика eSIM, а не поставщик. | |
name | string | — | |
generations | string[] | допускает null | Доступные поколения сети ( |
speed_cap_kbps | integer | допускает null | Ограничение скорости ВСЕГО тарифа, кбит/с, в отличие от |
price | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
tags | string[] | Нормализованные коммерческие метки: нижний регистр, дефисы. По ним также работают ограничения каталога ключа. Пока меток у тарифов нет, | |
validity | object | Срок действия тарифа и момент начала отсчёта. | |
days | integer | — | |
install_by_days | integer | допускает null | Число дней после покупки для сканирования QR и установки eSIM. |
label | string | — | |
reclaim_on_expiry | boolean | допускает null | Поставщик ИЗЫМАЕТ подписку после истечения тарифа: пополнение и смена становятся невозможны. Важный срок при продаже продлений. |
starts_when | string | допускает null | КОГДА начинается срок: on_installon_purchasenull |
unused_valid_days | integer | допускает null | Число дней хранения неиспользованного тарифа до истечения срока. |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
next_cursor | string | допускает null | Передайте БЕЗ ИЗМЕНЕНИЙ для следующей страницы. |
page_size | integer | допускает null | МАКСИМАЛЬНОЕ число записей, использованное сервером для страницы с учётом ограничения, а не фактическая длина |
total | integer · int64 | допускает null | Общее число тарифов по фильтрам. |
warnings | object[] | ПРИЧИНЫ состава списка. Пусто — особенностей нет. | |
code | string | —filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing | |
count | integer · int64 | допускает null | — |
message | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedinternal.invalid_filtercatalog.cursor_mismatchpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/catalog/packages/{code}Получить один тариф
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, как несуществующий.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedpartner.package_not_visiblepartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"data": null,
"ok": true
}Заказы
Создание заказов и сверка
/v1/ordersСписок заказов (сверка запросами)
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 предыдущей страницы БЕЗ ИЗМЕНЕНИЙ. ВНИМАНИЕ: неверный курсор не вызывает ошибку, сервер снова читает НАЧАЛО списка для совместимости при смене кодирования. Поэтому перед повтором проверяйте, что next_cursor отличается от отправленного курсора, иначе цикл будет бесконечно читать первую страницу.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
items | object[] | — | |
created_at | string · date-time | — | |
external_ref | string | допускает null | — |
fulfillment_status | string | —fulfilledpartially_fulfilledunfulfilled | |
id | string · uuid | — | |
item_count | integer · int64 | — | |
net | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
public_ref | string | допускает null | — |
refunded | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
status | string | — | |
total | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
updated_at | string · date-time | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
next_cursor | string | допускает null | Передайте БЕЗ ИЗМЕНЕНИЙ для следующей страницы. |
page_size | integer | допускает null | МАКСИМАЛЬНОЕ число записей, использованное сервером для страницы с учётом ограничения, а не фактическая длина |
total | integer · int64 | допускает null | Число строк по фильтру. |
warnings | object[] | ПРИЧИНЫ состава списка. Пусто — особенностей нет. | |
code | string | —filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing | |
count | integer · int64 | допускает null | — |
message | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedinternal.invalid_filterpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/ordersСоздать заказ (прямая покупка)
orders:writeТребуется Idempotency-Keypost_ordersТребуемая область доступа: orders:write.
Заголовки (1)
Idempotency-KeystringобязательноСтрока клиента: одинаковые ключ и тело выполняются ровно один раз; повтор возвращает исходный результат. Область действия — api_key_id (§11): разные ключи означают разные команды.
Тело запросаобязательноapplication/json
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
external_ref | string | допускает null | Ваша ссылка, повторяется в ответе и вебхуках. |
include | string[] | допускает null | Дополнительные блоки: |
invoice_buyer | object | допускает null | Только ключи участников. Без данных используется потребительский сценарий без организации или юридического лица во Вьетнаме. Переданные данные проверяются по текущим правилам: full_name, 12-значный персональный tax_code и address/email при оформлении. Корпоративный счёт запрашивается после покупки. Тестовый режим только проверяет снимок данных и не выпускает счета MISA. |
address | string | допускает null | — |
email | string | допускает null | — |
full_name | string | допускает null | — |
legal_name | string | допускает null | — |
tax_code | string | допускает null | — |
items | object[] | обязательно | Список тарифов и количества для покупки (не более 50 eSIM). |
package_code | string | обязательно | Код тарифа ВАШЕГО каталога, |
quantity | integer | обязательно | Количество eSIM этого тарифа для покупки (≥ 1). |
Ответ
Повтор ранее выполненной команды: те же Idempotency-Key и тело за 24 часа. Тело ответа ИДЕНТИЧНО первому, повторного списания НЕТ, добавляется Idempotent-Replayed: true. НОВАЯ операция возвращает 201; не определяйте успех только по этому статусу.
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | Заказ партнёра. |
created_at | string · date-time | — | |
external_ref | string | допускает null | — |
fulfillment_status | string | Фактически выданное количество — ОТДЕЛЬНО от fulfilledpartially_fulfilledunfulfilled | |
id | string · uuid | ID заказа UUID. | |
idempotent_replay | boolean | допускает null | true при идемпотентном повторе предыдущего успешного запроса. |
invoice_no | string | допускает null | Номер счёта после выпуска. |
invoice_status | string | Статус счёта с НДС заказа. nonependingissuingissuedfailedcancelled | |
items | object[] | — | |
cancellable | object | Можно ли ещё отменить eSIM и почему нельзя. Прочитайте ДО POST /v1/esims/{order_item_id}/cancel. Блок присутствует ВСЕГДА и никогда не | |
allowed | boolean | — | |
disclosed_at | string · date-time | допускает null | Время выдачи кода за пределы системы (ISO-8601 UTC), |
reason | string | допускает null | Причина запрета отмены: code_disclosedwrong_statusexpirednot_supportednull |
external_ref | string | допускает null | Ваша ссылка заказа |
iccid | string | допускает null | — |
id | string · uuid | ID eSIM для всех путей | |
install | object | допускает null | Данные для УСТАНОВКИ eSIM. |
android_url | string | допускает null | — |
apn | string | допускает null | APN для ручного ввода, если устройство не получает настройки автоматически. В текущей версии ВСЕГДА |
ios_url | string | допускает null | — |
lpa | string | допускает null | Код активации eSIM — секрет продажи. Этой строки достаточно для создания QR-кода у вас. |
matching_id | string | допускает null | — |
qr_code_base64 | string | допускает null | PNG data-URI QR-кода. Только при |
smdp_address | string | допускает null | — |
msisdn | string | допускает null | — |
order_id | string · uuid | — | |
package_code | string | допускает null | КОД тарифа из каталога для связи eSIM с вашим каталогом. |
package_name | string | Отображаемое НАЗВАНИЕ может меняться. Не используйте его как ключ сверки. | |
plan | object | допускает null | Текущий тариф этой eSIM. |
current_code | string | — | |
expires_at | string · date-time | допускает null | — |
started_at | string · date-time | допускает null | — |
smdp_status | string | допускает null | — |
status | string | — | |
updated_at | string · date-time | — | |
usage | object | допускает null | Статистика использованных данных eSIM. |
expires_at | string · date-time | допускает null | — |
remaining_bytes | integer · int64 | допускает null | — |
source | string | допускает null | —pollwebhook |
total_bytes | integer · int64 | допускает null | — |
updated_at | string · date-time | допускает null | — |
used_bytes | integer · int64 | допускает null | — |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
money_settled | boolean | Завершены ли расчёты по заказу, без возврата в обработке. При | |
net | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
public_ref | string | допускает null | Короткий понятный код заказа для обращения в поддержку. |
refunded | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
status | string | —completedpending_providerprovider_failedcancelled | |
total | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
ok | boolean | обязательно | —true |
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | Заказ партнёра. |
created_at | string · date-time | — | |
external_ref | string | допускает null | — |
fulfillment_status | string | Фактически выданное количество — ОТДЕЛЬНО от fulfilledpartially_fulfilledunfulfilled | |
id | string · uuid | ID заказа UUID. | |
idempotent_replay | boolean | допускает null | true при идемпотентном повторе предыдущего успешного запроса. |
invoice_no | string | допускает null | Номер счёта после выпуска. |
invoice_status | string | Статус счёта с НДС заказа. nonependingissuingissuedfailedcancelled | |
items | object[] | — | |
cancellable | object | Можно ли ещё отменить eSIM и почему нельзя. Прочитайте ДО POST /v1/esims/{order_item_id}/cancel. Блок присутствует ВСЕГДА и никогда не | |
allowed | boolean | — | |
disclosed_at | string · date-time | допускает null | Время выдачи кода за пределы системы (ISO-8601 UTC), |
reason | string | допускает null | Причина запрета отмены: code_disclosedwrong_statusexpirednot_supportednull |
external_ref | string | допускает null | Ваша ссылка заказа |
iccid | string | допускает null | — |
id | string · uuid | ID eSIM для всех путей | |
install | object | допускает null | Данные для УСТАНОВКИ eSIM. |
android_url | string | допускает null | — |
apn | string | допускает null | APN для ручного ввода, если устройство не получает настройки автоматически. В текущей версии ВСЕГДА |
ios_url | string | допускает null | — |
lpa | string | допускает null | Код активации eSIM — секрет продажи. Этой строки достаточно для создания QR-кода у вас. |
matching_id | string | допускает null | — |
qr_code_base64 | string | допускает null | PNG data-URI QR-кода. Только при |
smdp_address | string | допускает null | — |
msisdn | string | допускает null | — |
order_id | string · uuid | — | |
package_code | string | допускает null | КОД тарифа из каталога для связи eSIM с вашим каталогом. |
package_name | string | Отображаемое НАЗВАНИЕ может меняться. Не используйте его как ключ сверки. | |
plan | object | допускает null | Текущий тариф этой eSIM. |
current_code | string | — | |
expires_at | string · date-time | допускает null | — |
started_at | string · date-time | допускает null | — |
smdp_status | string | допускает null | — |
status | string | — | |
updated_at | string · date-time | — | |
usage | object | допускает null | Статистика использованных данных eSIM. |
expires_at | string · date-time | допускает null | — |
remaining_bytes | integer · int64 | допускает null | — |
source | string | допускает null | —pollwebhook |
total_bytes | integer · int64 | допускает null | — |
updated_at | string · date-time | допускает null | — |
used_bytes | integer · int64 | допускает null | — |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
money_settled | boolean | Завершены ли расчёты по заказу, без возврата в обработке. При | |
net | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
public_ref | string | допускает null | Короткий понятный код заказа для обращения в поддержку. |
refunded | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
status | string | —completedpending_providerprovider_failedcancelled | |
total | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedinternal.invalid_filterpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedwallet.insufficient_balancepartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedpartner.package_not_visibleesim.out_of_stockidempotency.conflictinvoice.buyer_invalidpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"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
}Пример тела запроса
{
"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
}
]
}/v1/orders/{id}Детали заказа и eSIM
orders:readget_orders__id_Требуемая область доступа: orders:read.
Параметры пути (1)
idstringобязательноID заказа UUID.
Параметры запроса (1)
includestringнеобязательноДополнительные блоки через запятую. lpa возвращает install с кодом активации; qr добавляет install.qr_code_base64 только в детальном ответе. ВОЗВРАТ КОДА СЧИТАЕТСЯ РАСКРЫТИЕМ. lpa НАВСЕГДА закрывает отмену всех eSIM ответа (cancellable.allowed = false, reason = code_disclosed): выданный код нельзя отозвать. Запрашивайте при передаче клиенту; при покупке заранее не запрашивайте, чтобы сохранить отмену. Неизвестное значение: internal.invalid_filter (400, field: "include").
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | Заказ партнёра. |
created_at | string · date-time | — | |
external_ref | string | допускает null | — |
fulfillment_status | string | Фактически выданное количество — ОТДЕЛЬНО от fulfilledpartially_fulfilledunfulfilled | |
id | string · uuid | ID заказа UUID. | |
idempotent_replay | boolean | допускает null | true при идемпотентном повторе предыдущего успешного запроса. |
invoice_no | string | допускает null | Номер счёта после выпуска. |
invoice_status | string | Статус счёта с НДС заказа. nonependingissuingissuedfailedcancelled | |
items | object[] | — | |
cancellable | object | Можно ли ещё отменить eSIM и почему нельзя. Прочитайте ДО POST /v1/esims/{order_item_id}/cancel. Блок присутствует ВСЕГДА и никогда не | |
allowed | boolean | — | |
disclosed_at | string · date-time | допускает null | Время выдачи кода за пределы системы (ISO-8601 UTC), |
reason | string | допускает null | Причина запрета отмены: code_disclosedwrong_statusexpirednot_supportednull |
external_ref | string | допускает null | Ваша ссылка заказа |
iccid | string | допускает null | — |
id | string · uuid | ID eSIM для всех путей | |
install | object | допускает null | Данные для УСТАНОВКИ eSIM. |
android_url | string | допускает null | — |
apn | string | допускает null | APN для ручного ввода, если устройство не получает настройки автоматически. В текущей версии ВСЕГДА |
ios_url | string | допускает null | — |
lpa | string | допускает null | Код активации eSIM — секрет продажи. Этой строки достаточно для создания QR-кода у вас. |
matching_id | string | допускает null | — |
qr_code_base64 | string | допускает null | PNG data-URI QR-кода. Только при |
smdp_address | string | допускает null | — |
msisdn | string | допускает null | — |
order_id | string · uuid | — | |
package_code | string | допускает null | КОД тарифа из каталога для связи eSIM с вашим каталогом. |
package_name | string | Отображаемое НАЗВАНИЕ может меняться. Не используйте его как ключ сверки. | |
plan | object | допускает null | Текущий тариф этой eSIM. |
current_code | string | — | |
expires_at | string · date-time | допускает null | — |
started_at | string · date-time | допускает null | — |
smdp_status | string | допускает null | — |
status | string | — | |
updated_at | string · date-time | — | |
usage | object | допускает null | Статистика использованных данных eSIM. |
expires_at | string · date-time | допускает null | — |
remaining_bytes | integer · int64 | допускает null | — |
source | string | допускает null | —pollwebhook |
total_bytes | integer · int64 | допускает null | — |
updated_at | string · date-time | допускает null | — |
used_bytes | integer · int64 | допускает null | — |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
money_settled | boolean | Завершены ли расчёты по заказу, без возврата в обработке. При | |
net | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
public_ref | string | допускает null | Короткий понятный код заказа для обращения в поддержку. |
refunded | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
status | string | —completedpending_providerprovider_failedcancelled | |
total | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedinternal.invalid_filterpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedinternal.not_foundpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/orders/{id}/resendПовторно отправить письмо/QR заказа
orders:writepost_orders__id__resendТребуемая область доступа: orders:write.
Параметры пути (1)
idstringобязательноID заказа UUID.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
id | string · uuid | ID заказа, данные которого только что отправлены повторно. | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
resent_at | string · date-time | — | |
resent_to | string | Адрес получателя, ЧАСТИЧНО СКРЫТЫЙ: партнёрский интерфейс не возвращает полный email клиента. | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedorder.not_deliverablepartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"data": {
"id": "018f2b00-0000-7000-8000-00000000009a",
"mode": "live",
"resent_at": "2026-07-11T08:12:30Z",
"resent_to": "ab***@x.com"
},
"ok": true
}/v1/orders/bulkСоздать пакет из нескольких строк (заказ на строку)
orders:writeТребуется Idempotency-Keypost_orders_bulkТребуемая область доступа: orders:write.
Создать пакет из нескольких строк: строка создаёт ОДИН независимый заказ тем же путём quote→commit, что POST /v1/orders. ЧАСТИЧНЫЙ УСПЕХ: ошибки не блокируют другие строки, списание только за успешные. Ошибки запаса, баланса, недоступности и лимитов — в lines[].error без смены HTTP-статуса. При нехватке баланса последующие более дешёвые строки всё равно проверяются. Статус всегда 200: ответ содержит результаты строк, не один ресурс и не отложенный процесс. Повтор после прерывания: ТЕ ЖЕ Idempotency-Key и тело загружают старый пакет, сохраняют завершённые результаты и выполняют только pending без двойного списания. Если строк не выполнялось, добавляется Idempotent-Replayed: true. Другое тело с тем же ключом: idempotency.conflict. Та же корзина за 10 минут в одной организации считается случайным дублем даже с разными ключами. Возвращается старый пакет с Idempotent-Replayed: true без списания. Для намеренной повторной покупки другому клиенту смените external_ref: он входит в подписанное тело, различает команды и помогает сверке. Полностью неуспешный пакет не блокируется; после 10 минут новый заказ разрешён. Ссылки строк возвращаются в lines[].external_ref, пакета — в корне. LPA/QR отсутствуют: получите eSIM через GET /v1/orders/{order_id} для каждого заказа.
Заголовки (1)
Idempotency-KeystringобязательноСтрока клиента: одинаковые ключ и тело выполняются ровно один раз; повтор возвращает исходный результат. Область действия — api_key_id (§11): разные ключи означают разные команды.
Тело запросаобязательноapplication/json
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
external_ref | string | допускает null | Ваша ссылка на ВЕСЬ пакет; возвращается в ответе пакета. |
items | object[] | обязательно | Каждый элемент создаёт ОДИН независимый заказ. Лимит строк настраивается (по умолчанию 100); превышение даёт |
external_ref | string | допускает null | Ваша ссылка ТОЛЬКО для этой строки; возвращается без изменений в результате строки. |
package_code | string | обязательно | Код тарифа ВАШЕГО каталога, |
quantity | integer | обязательно | — |
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | Результат пакета заказов с ЧАСТИЧНЫМ УСПЕХОМ: ошибки строк не блокируют остальные; деньги списываются ТОЛЬКО для |
created_at | string · date-time | — | |
external_ref | string | допускает null | — |
failed_lines | integer | — | |
id | string · uuid | — | |
items | object[] | — | |
charged | object | допускает null | — |
error | object | допускает null | Код ошибки |
code | string | — | |
message | string | — | |
external_ref | string | допускает null | — |
line_no | integer | Нумерация с 1, в порядке отправки. | |
order_id | string · uuid | допускает null | — |
package_code | string | — | |
quantity | integer | — | |
status | string | —pendingdonefailed | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
status | string |
pendingcompletedpartialfailed | |
succeeded_lines | integer | — | |
total_charged | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
total_lines | integer | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedpartner.package_not_visibleidempotency.conflictinternal.limit_exceededpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"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
}Пример тела запроса
{
"external_ref": "batch-2026-001",
"items": [
{
"external_ref": "po-1001",
"package_code": "TKE-VN-30D-X3K9F",
"quantity": 5
}
]
}/v1/orders/bulk/{id}Результат пакета заказов
orders:readget_orders_bulk__id_Требуемая область доступа: orders:read.
Повторное чтение результатов строк ранее созданного пакета при потере исходного ответа без повторной отправки тела. Пакет другой организации или режима (рабочий ⇄ тестовый) даёт 404.
Параметры пути (1)
idstringобязательноID пакета UUID, возвращённый POST /v1/orders/bulk.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | Результат пакета заказов с ЧАСТИЧНЫМ УСПЕХОМ: ошибки строк не блокируют остальные; деньги списываются ТОЛЬКО для |
created_at | string · date-time | — | |
external_ref | string | допускает null | — |
failed_lines | integer | — | |
id | string · uuid | — | |
items | object[] | — | |
charged | object | допускает null | — |
error | object | допускает null | Код ошибки |
code | string | — | |
message | string | — | |
external_ref | string | допускает null | — |
line_no | integer | Нумерация с 1, в порядке отправки. | |
order_id | string · uuid | допускает null | — |
package_code | string | — | |
quantity | integer | — | |
status | string | —pendingdonefailed | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
status | string |
pendingcompletedpartialfailed | |
succeeded_lines | integer | — | |
total_charged | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
total_lines | integer | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedinternal.not_foundpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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: записи, расход, пополнение и отмена
/v1/esimsСписок eSIM (сверка запросами)
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), а не пустой список: опечатка вроде canceled без второго l не должна выглядеть как отсутствие eSIM в этом состоянии.
order_idstring · uuidнеобязательноТолько eSIM одного заказа.
iccidstringнеобязательноФильтр по ICCID (строка, точное совпадение).
external_refstringнеобязательноВсе eSIM заказов с вашей ссылкой заказа (external_ref, переданной при покупке, точное совпадение).
updated_sincestring · date-timeнеобязательноISO-8601 UTC. Записи с updated_at >= updated_since по возрастанию — стандартная СВЕРКА ЗАПРОСАМИ (§8.5), позволяющая восстановить пропущенный вебхук по состоянию. Неверный формат: internal.invalid_filter (400).
cursorstringнеобязательноКурсор следующей страницы: передайте next_cursor предыдущей страницы БЕЗ ИЗМЕНЕНИЙ. ВНИМАНИЕ: неверный курсор не вызывает ошибку, сервер снова читает НАЧАЛО списка для совместимости при смене кодирования. Поэтому перед повтором проверяйте, что next_cursor отличается от отправленного курсора, иначе цикл будет бесконечно читать первую страницу.
includestringнеобязательноДополнительные блоки через запятую. lpa возвращает install с кодом активации; qr добавляет install.qr_code_base64 только в детальном ответе. ВОЗВРАТ КОДА СЧИТАЕТСЯ РАСКРЫТИЕМ. lpa НАВСЕГДА закрывает отмену всех eSIM ответа (cancellable.allowed = false, reason = code_disclosed): выданный код нельзя отозвать. Запрашивайте при передаче клиенту; при покупке заранее не запрашивайте, чтобы сохранить отмену. Неизвестное значение: internal.invalid_filter (400, field: "include").
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
items | object[] | — | |
cancellable | object | Можно ли ещё отменить eSIM и почему нельзя. Прочитайте ДО POST /v1/esims/{order_item_id}/cancel. Блок присутствует ВСЕГДА и никогда не | |
allowed | boolean | — | |
disclosed_at | string · date-time | допускает null | Время выдачи кода за пределы системы (ISO-8601 UTC), |
reason | string | допускает null | Причина запрета отмены: code_disclosedwrong_statusexpirednot_supportednull |
external_ref | string | допускает null | Ваша ссылка заказа |
iccid | string | допускает null | — |
id | string · uuid | ID eSIM для всех путей | |
install | object | допускает null | Данные для УСТАНОВКИ eSIM. |
android_url | string | допускает null | — |
apn | string | допускает null | APN для ручного ввода, если устройство не получает настройки автоматически. В текущей версии ВСЕГДА |
ios_url | string | допускает null | — |
lpa | string | допускает null | Код активации eSIM — секрет продажи. Этой строки достаточно для создания QR-кода у вас. |
matching_id | string | допускает null | — |
qr_code_base64 | string | допускает null | PNG data-URI QR-кода. Только при |
smdp_address | string | допускает null | — |
msisdn | string | допускает null | — |
order_id | string · uuid | — | |
package_code | string | допускает null | КОД тарифа из каталога для связи eSIM с вашим каталогом. |
package_name | string | Отображаемое НАЗВАНИЕ может меняться. Не используйте его как ключ сверки. | |
plan | object | допускает null | Текущий тариф этой eSIM. |
current_code | string | — | |
expires_at | string · date-time | допускает null | — |
started_at | string · date-time | допускает null | — |
smdp_status | string | допускает null | — |
status | string | — | |
updated_at | string · date-time | — | |
usage | object | допускает null | Статистика использованных данных eSIM. |
expires_at | string · date-time | допускает null | — |
remaining_bytes | integer · int64 | допускает null | — |
source | string | допускает null | —pollwebhook |
total_bytes | integer · int64 | допускает null | — |
updated_at | string · date-time | допускает null | — |
used_bytes | integer · int64 | допускает null | — |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
next_cursor | string | допускает null | Передайте БЕЗ ИЗМЕНЕНИЙ для следующей страницы. |
page_size | integer | допускает null | МАКСИМАЛЬНОЕ число записей, использованное сервером для страницы с учётом ограничения, а не фактическая длина |
total | integer · int64 | допускает null | Число строк по фильтру. |
warnings | object[] | ПРИЧИНЫ состава списка. Пусто — особенностей нет. | |
code | string | —filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing | |
count | integer · int64 | допускает null | — |
message | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedinternal.invalid_filterpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/esims/{id}Получить одну eSIM
esims:readget_esims__id_Требуемая область доступа: esims:read.
Параметры пути (1)
idstringобязательноПостоянный ID проданной eSIM, НЕ ICCID, который может повторно использоваться.
Параметры запроса (1)
includestringнеобязательноДополнительные блоки через запятую. lpa возвращает install с кодом активации; qr добавляет install.qr_code_base64 только в детальном ответе. ВОЗВРАТ КОДА СЧИТАЕТСЯ РАСКРЫТИЕМ. lpa НАВСЕГДА закрывает отмену всех eSIM ответа (cancellable.allowed = false, reason = code_disclosed): выданный код нельзя отозвать. Запрашивайте при передаче клиенту; при покупке заранее не запрашивайте, чтобы сохранить отмену. Неизвестное значение: internal.invalid_filter (400, field: "include").
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
cancellable | object | Можно ли ещё отменить eSIM и почему нельзя. Прочитайте ДО POST /v1/esims/{order_item_id}/cancel. Блок присутствует ВСЕГДА и никогда не | |
allowed | boolean | — | |
disclosed_at | string · date-time | допускает null | Время выдачи кода за пределы системы (ISO-8601 UTC), |
reason | string | допускает null | Причина запрета отмены: code_disclosedwrong_statusexpirednot_supportednull |
external_ref | string | допускает null | Ваша ссылка заказа |
iccid | string | допускает null | — |
id | string · uuid | ID eSIM для всех путей | |
install | object | допускает null | Данные для УСТАНОВКИ eSIM. |
android_url | string | допускает null | — |
apn | string | допускает null | APN для ручного ввода, если устройство не получает настройки автоматически. В текущей версии ВСЕГДА |
ios_url | string | допускает null | — |
lpa | string | допускает null | Код активации eSIM — секрет продажи. Этой строки достаточно для создания QR-кода у вас. |
matching_id | string | допускает null | — |
qr_code_base64 | string | допускает null | PNG data-URI QR-кода. Только при |
smdp_address | string | допускает null | — |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
msisdn | string | допускает null | — |
order_id | string · uuid | — | |
package_code | string | допускает null | КОД тарифа из каталога для связи eSIM с вашим каталогом. |
package_name | string | Отображаемое НАЗВАНИЕ может меняться. Не используйте его как ключ сверки. | |
plan | object | допускает null | Текущий тариф этой eSIM. |
current_code | string | — | |
expires_at | string · date-time | допускает null | — |
started_at | string · date-time | допускает null | — |
smdp_status | string | допускает null | — |
status | string | — | |
updated_at | string · date-time | — | |
usage | object | допускает null | Статистика использованных данных eSIM. |
expires_at | string · date-time | допускает null | — |
remaining_bytes | integer · int64 | допускает null | — |
source | string | допускает null | —pollwebhook |
total_bytes | integer · int64 | допускает null | — |
updated_at | string · date-time | допускает null | — |
used_bytes | integer · int64 | допускает null | — |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedinternal.invalid_filterpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedinternal.not_foundpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/esims/{id}/cancelОтменить неиспользованную eSIM
esims:writeТребуется Idempotency-Keypost_esims__id__cancelТребуемая область доступа: esims:write.
Параметры пути (1)
idstringобязательноПостоянный ID проданной eSIM, НЕ ICCID, который может повторно использоваться.
Заголовки (1)
Idempotency-KeystringобязательноСтрока клиента: одинаковые ключ и тело выполняются ровно один раз; повтор возвращает исходный результат. Область действия — api_key_id (§11): разные ключи означают разные команды.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
cancelled_at | string · date-time | — | |
esim_id | string · uuid | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
refund_suggested | boolean |
| |
refunded | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
status | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedesim.not_cancellableidempotency.conflictcapability.not_supportedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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)"Пример ответа
{
"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
}/v1/esims/{id}/change-planПополнить eSIM
esims:writeТребуется Idempotency-Keypost_esims__id__change-planТребуемая область доступа: esims:write.
Параметры пути (1)
idstringобязательноПостоянный ID проданной eSIM, НЕ ICCID, который может повторно использоваться.
Заголовки (1)
Idempotency-KeystringобязательноСтрока клиента: одинаковые ключ и тело выполняются ровно один раз; повтор возвращает исходный результат. Область действия — api_key_id (§11): разные ключи означают разные команды.
Тело запросаобязательноapplication/json
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
package_code | string | обязательно | Код тарифа ВАШЕГО каталога, |
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | Результат смены тарифа eSIM. |
charged | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
current_plan | object | — | |
code | string | — | |
expires_at | string · date-time | допускает null | — |
started_at | string · date-time | допускает null | — |
esim_id | string · uuid | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
order_id | string · uuid | — | |
previous_plan | object | допускает null | — |
cancelled_at | string · date-time | допускает null | — |
code | string | — | |
remaining_bytes_forfeited | integer · int64 | допускает null | — |
warning | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedwallet.insufficient_balancepartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedpartner.package_not_visibleesim.topup_not_supportedesim.topup_provider_mismatchesim.not_eligibleidempotency.conflictcapability.not_supportedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"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
}Пример тела запроса
{
"package_code": "TKE-VN-7D-P4M2Q"
}/v1/esims/{id}/plan-optionsДоступные тарифы пополнения этой eSIM
esims:readget_esims__id__plan-optionsТребуемая область доступа: esims:read.
Доступные тарифы пополнения ИМЕННО ЭТОЙ eSIM. Сервер находит её поставщика по id и возвращает только его тарифы. /v1 не раскрывает provider_id, поэтому фильтрация исключает выбор другого поставщика. Прямой POST /v1/esims/{id}/change-plan с тарифом вне списка даёт 409 esim.topup_provider_mismatch. Без связанного поставщика (импортированная запись или отсутствующие исходные данные) возвращается ПУСТОЙ список total: 0; это корректный ответ.
Параметры пути (1)
idstringобязательноПостоянный ID проданной eSIM, НЕ ICCID, который может повторно использоваться.
Параметры запроса (2)
page_sizeintegerнеобязательнопо умолчанию 50min 1max 200Записей на странице. По умолчанию 50, максимум 200. Большее значение ограничивается 200, это НЕ ошибка.
cursorstringнеобязательноКурсор следующей страницы: передайте next_cursor предыдущей страницы БЕЗ ИЗМЕНЕНИЙ. ВНИМАНИЕ: неверный курсор не вызывает ошибку, сервер снова читает НАЧАЛО списка для совместимости при смене кодирования. Поэтому перед повтором проверяйте, что next_cursor отличается от отправленного курсора, иначе цикл будет бесконечно читать первую страницу.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | Тарифы пополнения ЭТОЙ eSIM, уже отфильтрованные по её поставщику. Каждый элемент — полный |
items | object[] | — | |
capabilities | object | Доступные действия ПОСЛЕ продажи из РЕАЛЬНОЙ декларации тарифа, без предположений по типу поставщика. Без декларации все флаги | |
cancel | object | — | |
restriction | string | допускает null |
code_not_disclosednull |
supported | boolean | — | |
plan_change | object | — | |
policy | object | допускает null | Политика ДОБАВЛЕНИЯ НОВОГО ТАРИФА поставщика. |
… | … | Вложенные поля смотрите в примере JSON. | |
supported | boolean | — | |
usage_check | object | — | |
supported | boolean | — | |
code | string | Код тарифа ВАШЕГО каталога, | |
coverage | object | Где работает тариф. | |
countries | string[] | Коды ISO-3166 alpha-2. Пустой массив у глобальных тарифов (200 стран не перечисляются) и тарифов без заявленного покрытия. | |
country_count | integer | Количество стран покрытия для сортировки или фильтрации многострановых тарифов без загрузки и подсчёта всего массива. | |
group | string | Тип покрытия: страны | |
region | string | допускает null | Пока ВСЕГДА |
data | object | Лимит данных тарифа. ВСЕ значения в БАЙТАХ, не МБ/ГБ, чтобы избежать двойного округления. | |
daily_bytes | integer · int64 | допускает null | ДНЕВНОЙ лимит данных в байтах. |
label | string | Понятная пользователю подпись, созданная общей для всех экранов функцией. | |
model | string | Учёт лимита: dailyfixedunlimited | |
reset | object | допускает null | Момент сброса дневного лимита. Неизвестное время может лишить купившего в 22:00 почти всего первого дня. |
at | string | допускает null | Местное время сброса в часовом поясе |
note | string | допускает null | — |
timezone | string | допускает null | Часовой пояс сброса ( |
total_bytes | integer · int64 | допускает null | Общий лимит на весь срок в байтах. |
description | string | допускает null | Описание тарифа для продажи. |
description_en | string | допускает null | Описание на английском. |
features | object | Возможности тарифа. ТИПИЗИРОВАННЫЙ объект: массив | |
apps | object[] | Поддержка приложения по ОПЕРАЦИОННЫМ СИСТЕМАМ. Отсутствие приложения в массиве означает ОТСУТСТВИЕ ДАННЫХ, а не поддержку. Поставщик гарантирует только перечисленное; пока заявлен только TikTok. Объединённого флага нет намеренно: в реальном каталоге 2465 тарифов поддерживают TikTok на iPhone, но не Android. Единый флаг исказил бы почти треть каталога. | |
android | string |
supportedblockedunknown | |
app | string | Код приложения в нижнем регистре, например | |
ios | string |
supportedblockedunknown | |
note | string | допускает null | — |
hotspot | boolean | допускает null | Раздача Wi-Fi другим устройствам. |
kyc_required | boolean | допускает null | Перед использованием требуется проверка личности KYC. |
sms | boolean | допускает null | SMS. |
voice | boolean | допускает null | Голосовые вызовы. |
fup | object | допускает null | Поведение после исчерпания скоростного лимита. |
behavior | string | допускает null |
throttlecutoffnull |
high_speed_bytes | integer · int64 | допускает null | Объём в БАЙТАХ на полной скорости до ограничения. |
note | string | допускает null | — |
speed_kbps | integer | допускает null | Скорость ПОСЛЕ исчерпания скоростного лимита, кбит/с. |
market | string | СИСТЕМА ПОСТАВКИ, ОТКРЫТОЕ перечисление: игнорируйте неизвестные значения. Определяет запас и действия после продажи: domesticinternational | |
name | string | — | |
name_en | string | допускает null | Название на английском. |
network | object | Сети, в которых работает тариф. | |
apn | string | допускает null | APN для ручного ввода на Android, если устройство не подхватило его автоматически. Требуется для инструкции установки, иначе клиенту понадобится поддержка. |
carriers | object[] | Операторы связи, В СЕТЯХ которых работает тариф. Характеристика eSIM, а не поставщик. | |
name | string | — | |
generations | string[] | допускает null | Доступные поколения сети ( |
speed_cap_kbps | integer | допускает null | Ограничение скорости ВСЕГО тарифа, кбит/с, в отличие от |
price | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
tags | string[] | Нормализованные коммерческие метки: нижний регистр, дефисы. По ним также работают ограничения каталога ключа. Пока меток у тарифов нет, | |
validity | object | Срок действия тарифа и момент начала отсчёта. | |
days | integer | — | |
install_by_days | integer | допускает null | Число дней после покупки для сканирования QR и установки eSIM. |
label | string | — | |
reclaim_on_expiry | boolean | допускает null | Поставщик ИЗЫМАЕТ подписку после истечения тарифа: пополнение и смена становятся невозможны. Важный срок при продаже продлений. |
starts_when | string | допускает null | КОГДА начинается срок: on_installon_purchasenull |
unused_valid_days | integer | допускает null | Число дней хранения неиспользованного тарифа до истечения срока. |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
next_cursor | string | допускает null | Передайте БЕЗ ИЗМЕНЕНИЙ для следующей страницы. |
page_size | integer | МАКСИМАЛЬНОЕ число записей, использованное сервером для страницы с учётом ограничения, а не фактическая длина | |
policy | object | допускает null | Политика ДОБАВЛЕНИЯ НОВОГО ТАРИФА поставщика. |
note | string | допускает null | — |
requires_cancel_first | boolean | ТЕКУЩИЙ тариф отменяется до добавления нового: этот поставщик не складывает тарифы. Сообщите клиенту ДО пополнения новым тарифом. | |
stacking | boolean | Добавляются ли срок и лимит нового тарифа к текущему. | |
total | integer · int64 | допускает null | Общее число тарифов по фильтрам. |
warnings | object[] | ПРИЧИНЫ состава списка. Пусто — особенностей нет. | |
code | string | —filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing | |
count | integer · int64 | допускает null | — |
message | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedinternal.not_foundpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/esims/{id}/refund-requestsИстория заявок на возврат eSIM (только участники)
esims:readget_esims__id__refund-requestsТребуемая область доступа: esims:read.
Только разрешённые рабочие ключи участников. Возвращает до 50 последних заявок по eSIM участника, начиная с новых; next_cursor и total равны null. has_open_request указывает на ожидание проверки. Ключи организаций и тестовые ключи получают capability.not_supported.
Параметры пути (1)
idstringобязательноПостоянный ID проданной eSIM, НЕ ICCID, который может повторно использоваться.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
has_open_request | boolean | — | |
items | object[] | — | |
decided_at | string · date-time | допускает null | — |
decision_note | string | допускает null | — |
id | string · uuid | — | |
order_id | string · uuid | — | |
order_item_id | string · uuid | — | |
reason | string | — | |
refunded | object | допускает null | — |
requested_at | string · date-time | — | |
status | string | —pendingapprovedrejected | |
suggested | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
next_cursor | string | допускает null | Передайте БЕЗ ИЗМЕНЕНИЙ для следующей страницы. |
page_size | integer | допускает null | МАКСИМАЛЬНОЕ число записей, использованное сервером для страницы с учётом ограничения, а не фактическая длина |
total | integer · int64 | допускает null | Число строк по фильтру. |
warnings | object[] | ПРИЧИНЫ состава списка. Пусто — особенностей нет. | |
code | string | —filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing | |
count | integer · int64 | допускает null | — |
message | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedinternal.not_foundcapability.not_supportedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/esims/{id}/refund-requestsСоздать заявку на возврат eSIM (только участники)
esims:writeТребуется Idempotency-Keypost_esims__id__refund-requestsТребуемая область доступа: esims:write.
Только разрешённые рабочие ключи участников. Заявка на возврат за собственную eSIM требует Idempotency-Key; средства не переводятся до одобрения администратора. Ключи организаций и тестовые ключи получают capability.not_supported. Для проверки виртуального возврата в тестовом режиме отмените eSIM.
Параметры пути (1)
idstringобязательноПостоянный ID проданной eSIM, НЕ ICCID, который может повторно использоваться.
Заголовки (1)
Idempotency-KeystringобязательноСтрока клиента: одинаковые ключ и тело выполняются ровно один раз; повтор возвращает исходный результат. Область действия — api_key_id (§11): разные ключи означают разные команды.
Тело запросаобязательноapplication/json
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
reason | string | обязательно | Причина возврата, от 5 до 1000 символов после удаления пробелов по краям. |
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
decided_at | string · date-time | допускает null | — |
decision_note | string | допускает null | — |
id | string · uuid | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
order_id | string · uuid | — | |
order_item_id | string · uuid | — | |
reason | string | — | |
refunded | object | допускает null | — |
requested_at | string · date-time | — | |
status | string | —pendingapprovedrejected | |
suggested | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedinternal.not_foundesim.refund_not_eligibleesim.refund_request_pendingidempotency.conflictcapability.not_supportedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"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
}Пример тела запроса
{
"reason": "<reason>"
}/v1/esims/{id}/usageИспользованные данные
esims:readget_esims__id__usageТребуемая область доступа: esims:read.
Использованные и оставшиеся данные eSIM с updated_at, показывающим фактическую свежесть. Для поставщиков с опросом по запросу (сейчас SkyFi) вызов ставит опрос в очередь и ЖДЁТ до 15 секунд. Успешный ответ содержит свежий результат. При ошибке, интервале менее 15 секунд между опросами одной eSIM или тайм-ауте ошибки НЕТ: возвращаются последние сохранённые данные, проверяйте updated_at. Другие поставщики возвращают данные периодического обновления.
Параметры пути (1)
idstringобязательноПостоянный ID проданной eSIM, НЕ ICCID, который может повторно использоваться.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
esim_id | string · uuid | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
usage | object | допускает null | Статистика использованных данных eSIM. |
expires_at | string · date-time | допускает null | — |
remaining_bytes | integer · int64 | допускает null | — |
source | string | допускает null | —pollwebhook |
total_bytes | integer · int64 | допускает null | — |
updated_at | string · date-time | допускает null | — |
used_bytes | integer · int64 | допускает null | — |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedcapability.not_supportedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}Кошелёк
Баланс кошелька
/v1/wallet/balanceБаланс кошелька
wallet:readget_wallet_balanceТребуемая область доступа: wallet:read.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
available | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
balance | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
credit_limit | object | Денежная сумма. ЕДИНЫЙ формат во всём | |
amount | integer · int64 | Целое число в минимальной единице | |
currency | string | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"data": {
"available": {
"amount": 273000,
"currency": "VND"
},
"balance": {
"amount": 273000,
"currency": "VND"
},
"credit_limit": {
"amount": 273000,
"currency": "VND"
},
"mode": "live"
},
"ok": true
}Вебхуки
Настройки вебхуков, журнал доставок и симулятор
/v1/testmode/webhooks/fail-nextСимулировать следующую ошибку вебхука (только тестовые ключи)
webhook:managepost_testmode_webhooks_fail-nextТребуемая область доступа: webhook:manage.
Тело запросаобязательноapplication/json
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
count | integer | — |
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
endpoint_id | string · uuid | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
testmode_fail_next | integer | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbiddeninternal.not_foundpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"data": {
"endpoint_id": "018f2b00-0000-7000-8000-000000000000",
"mode": "live",
"testmode_fail_next": 1
},
"ok": true
}Пример тела запроса
{
"count": 1
}/v1/testmode/webhooks/fireОтправить тестовое событие вебхука (только тестовые ключи)
webhook:managepost_testmode_webhooks_fireТребуемая область доступа: webhook:manage.
Тело запросаобязательноapplication/json
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | Частичное переопределение поля | |
event_type | string | обязательно | Тип тестового события вебхука. order.completedorder.failedesim.deliveredesim.activatedesim.usage_thresholdesim.expiringwallet.low_balancecatalog.updated |
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
delivery_id | string · uuid | — | |
event_id | string · uuid | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbiddenwebhook.not_configuredinternal.not_foundpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"data": {
"delivery_id": "018f2b00-0000-7000-8000-000000000000",
"event_id": "018f3c00-0000-7000-8000-0000000000b0",
"mode": "live"
},
"ok": true
}Пример тела запроса
{
"data": {},
"event_type": "order.completed"
}/v1/webhooksСписок обработчиков вебхуков
webhook:manageget_webhooksТребуемая область доступа: webhook:manage.
Обработчики вебхуков текущего API-ключа. Ключ НИКОГДА не видит обработчики другого, даже в одной организации (§5.11, привязка к api_key_id; тестовый и рабочий ключи разделены). Если настройка ещё не создана, возвращается 200 с ПУСТЫМ массивом, не 404.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
items | object[] | — | |
active | boolean |
| |
created_at | string · date-time | — | |
events | string[] | Пусто — получать все события. | |
id | string · uuid | ID обработчика для | |
last_delivery_at | string · date-time | допускает null | Последнее время УСПЕШНОЙ ДОСТАВКИ обработчику. |
secret_last4 | string | Последние четыре символа секрета вебхука для проверки соответствия без раскрытия секрета. Полный секрет показывается ОДИН РАЗ при создании. | |
url | string | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
next_cursor | string | допускает null | Передайте БЕЗ ИЗМЕНЕНИЙ для следующей страницы. |
page_size | integer | допускает null | МАКСИМАЛЬНОЕ число записей, использованное сервером для страницы с учётом ограничения, а не фактическая длина |
total | integer · int64 | допускает null | Число строк по фильтру. |
warnings | object[] | ПРИЧИНЫ состава списка. Пусто — особенностей нет. | |
code | string | —filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing | |
count | integer · int64 | допускает null | — |
message | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/webhooksСоздать обработчик вебхука
webhook:managepost_webhooksТребуемая область доступа: webhook:manage.
Создать обработчик вебхука для текущего API-ключа. До сохранения сервер отправляет подписанный ping на указанный URL (CHECK_HEALTH). Без 2xx ничего не сохраняется: 422 webhook.health_check_failed. URL — публичный https; внутренний или metadata 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
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
events | string[] | Пусто — получать все события. Неизвестное значение: 400 | |
url | string · uri | обязательно | — |
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
active | boolean |
| |
created_at | string · date-time | — | |
events | string[] | Пусто — получать все события. | |
health_check | string | —passed | |
id | string · uuid | ID обработчика для | |
last_delivery_at | string · date-time | допускает null | Последнее время УСПЕШНОЙ ДОСТАВКИ обработчику. |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
secret_last4 | string | Последние четыре символа секрета вебхука для проверки соответствия без раскрытия секрета. Полный секрет показывается ОДИН РАЗ при создании. | |
url | string | — | |
webhook_secret | string | Показывается ТОЛЬКО ОДИН РАЗ при создании (префикс | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedwebhook.already_configuredwebhook.health_check_failedwebhook.url_not_allowedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"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
}Пример тела запроса
{
"events": [
"order.completed"
],
"url": "https://partner.example.com/hooks/tke"
}/v1/webhooks/{id}Получить один обработчик вебхука
webhook:manageget_webhooks__id_Требуемая область доступа: webhook:manage.
Параметры пути (1)
idstringобязательноID обработчика вебхука: id из POST /v1/webhooks или GET /v1/webhooks.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
active | boolean |
| |
created_at | string · date-time | — | |
events | string[] | Пусто — получать все события. | |
id | string · uuid | ID обработчика для | |
last_delivery_at | string · date-time | допускает null | Последнее время УСПЕШНОЙ ДОСТАВКИ обработчику. |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
secret_last4 | string | Последние четыре символа секрета вебхука для проверки соответствия без раскрытия секрета. Полный секрет показывается ОДИН РАЗ при создании. | |
url | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedwebhook.not_foundpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/webhooks/{id}Обновить обработчик вебхука
webhook:manageput_webhooks__id_Требуемая область доступа: webhook:manage.
Параметры пути (1)
idstringобязательноID обработчика вебхука: id из POST /v1/webhooks или GET /v1/webhooks.
Тело запросаобязательноapplication/json
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
events | string[] | Пусто — получать все события. Неизвестное значение: 400 | |
url | string · uri | обязательно | — |
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
active | boolean |
| |
created_at | string · date-time | — | |
events | string[] | Пусто — получать все события. | |
id | string · uuid | ID обработчика для | |
last_delivery_at | string · date-time | допускает null | Последнее время УСПЕШНОЙ ДОСТАВКИ обработчику. |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
secret_last4 | string | Последние четыре символа секрета вебхука для проверки соответствия без раскрытия секрета. Полный секрет показывается ОДИН РАЗ при создании. | |
url | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedwebhook.not_foundwebhook.health_check_failedwebhook.url_not_allowedpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"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
}Пример тела запроса
{
"events": [
"order.completed"
],
"url": "https://partner.example.com/hooks/tke"
}/v1/webhooks/{id}Удалить обработчик вебхука (прекратить новые события)
webhook:managedelete_webhooks__id_Требуемая область доступа: webhook:manage.
Удалить обработчик и прекратить новые события текущего API-ключа. Теряется webhook_secret: повторный POST /v1/webhooks создаёт НОВЫЕ id и секрет, проверку подписи нужно обновить. Для временной остановки с сохранением секрета используйте кнопку паузы в панели. Сохраняется журнал для сверки, но переотправлять доставки удалённого обработчика нельзя. Ожидающие pending доставки останавливаются с endpoint_disabled и остаются в журнале (§8.5). Чужой или уже удалённый id даёт 404 webhook.not_found, не 200.
Параметры пути (1)
idstringобязательноID обработчика вебхука: id из POST /v1/webhooks или GET /v1/webhooks.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
deleted_at | string · date-time | — | |
id | string · uuid | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedwebhook.not_foundpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"data": {
"deleted_at": "2026-07-11T08:12:30Z",
"id": "018f2b00-0000-7000-8000-00000000009a",
"mode": "live"
},
"ok": true
}/v1/webhooks/deliveriesЖурнал доставок вебхуков
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необязательноВсе попытки доставки ОДНОГО события, включая переотправки.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
items | object[] | — | |
attempts | integer | — | |
created_at | string · date-time | — | |
event_id | string · uuid | Идентификатор СОБЫТИЯ. Переотправка создаёт НОВУЮ доставку с ТЕМ ЖЕ | |
event_type | string | — | |
id | string · uuid | — | |
last_attempt_at | string · date-time | допускает null | — |
next_retry_at | string · date-time | допускает null | — |
response_code | integer | допускает null | — |
status | string | —pendingdeliveredfailed | |
webhook_id | string · uuid | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
next_cursor | string | допускает null | Передайте БЕЗ ИЗМЕНЕНИЙ для следующей страницы. |
page_size | integer | допускает null | МАКСИМАЛЬНОЕ число записей, использованное сервером для страницы с учётом ограничения, а не фактическая длина |
total | integer · int64 | допускает null | Число строк по фильтру. |
warnings | object[] | ПРИЧИНЫ состава списка. Пусто — особенностей нет. | |
code | string | —filtered_by_key_package_filtersfiltered_by_provider_scopeno_price_for_orgprice_filter_removed_rowsprovider_disabledcapability_missing | |
count | integer · int64 | допускает null | — |
message | string | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedwebhook.not_configuredpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}/v1/webhooks/deliveries/{id}/replayПовторно отправить вебхук
webhook:managepost_webhooks_deliveries__id__replayТребуемая область доступа: webhook:manage.
Параметры пути (1)
idstringобязательноID одной доставки вебхука (items[].id из GET /v1/webhooks/deliveries).
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
attempts | integer | — | |
created_at | string · date-time | — | |
event_id | string · uuid | Идентификатор СОБЫТИЯ. Переотправка создаёт НОВУЮ доставку с ТЕМ ЖЕ | |
event_type | string | — | |
id | string · uuid | — | |
last_attempt_at | string · date-time | допускает null | — |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
next_retry_at | string · date-time | допускает null | — |
response_code | integer | допускает null | — |
status | string | —pendingdeliveredfailed | |
webhook_id | string · uuid | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedwebhook.delivery_not_foundwebhook.not_configuredpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}Тестовый режим
Используйте только тестовые ключи tke_test_ или tke_member_test_ на хосте api-testing. Моделируются тестовые eSIM и данные владельца без реальных средств и поставщиков. Для участников не поддерживаются вебхуки, пакетные заказы и заявки на ручное рассмотрение возврата.
/v1/testmode/esims/{id}/simulateСимулировать жизненный цикл тестовой eSIM (только тестовые ключи)
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обязательноПостоянный ID проданной eSIM, НЕ ICCID, который может повторно использоваться.
Тело запросаобязательноapplication/json
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data_used_mb | integer · int64 | УЖЕ ИСПОЛЬЗОВАННЫЕ данные в МБ (двоичные единицы 1 MiB). Значение выше лимита ограничивается лимитом: это сценарий исчерпания данных, а не ошибка. | |
expire_at | string · date-time | Задать точное время истечения срока (ISO-8601 UTC). | |
expire_now | boolean |
| |
provider_reject | boolean | Симулировать отказ поставщика при следующем пополнении/отмене. | |
status | string | ТЕХНИЧЕСКИЙ жизненный цикл профиля eSIM по терминологии поставщика, ОТЛИЧНЫЙ от коммерческого releasedinstalledenableddisabled | |
throttled | boolean |
|
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
cancellable | object | Можно ли ещё отменить eSIM и почему нельзя. Прочитайте ДО POST /v1/esims/{order_item_id}/cancel. Блок присутствует ВСЕГДА и никогда не | |
allowed | boolean | — | |
disclosed_at | string · date-time | допускает null | Время выдачи кода за пределы системы (ISO-8601 UTC), |
reason | string | допускает null | Причина запрета отмены: code_disclosedwrong_statusexpirednot_supportednull |
external_ref | string | допускает null | Ваша ссылка заказа |
iccid | string | допускает null | — |
id | string · uuid | ID eSIM для всех путей | |
install | object | допускает null | Данные для УСТАНОВКИ eSIM. |
android_url | string | допускает null | — |
apn | string | допускает null | APN для ручного ввода, если устройство не получает настройки автоматически. В текущей версии ВСЕГДА |
ios_url | string | допускает null | — |
lpa | string | допускает null | Код активации eSIM — секрет продажи. Этой строки достаточно для создания QR-кода у вас. |
matching_id | string | допускает null | — |
qr_code_base64 | string | допускает null | PNG data-URI QR-кода. Только при |
smdp_address | string | допускает null | — |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
msisdn | string | допускает null | — |
order_id | string · uuid | — | |
package_code | string | допускает null | КОД тарифа из каталога для связи eSIM с вашим каталогом. |
package_name | string | Отображаемое НАЗВАНИЕ может меняться. Не используйте его как ключ сверки. | |
plan | object | допускает null | Текущий тариф этой eSIM. |
current_code | string | — | |
expires_at | string · date-time | допускает null | — |
started_at | string · date-time | допускает null | — |
simulated | object | Состояние ПОСЛЕ применения сценария. | |
esim_state | string | ТЕХНИЧЕСКИЙ цикл тестовой eSIM. У рабочей eSIM он берётся из releasedinstalledenableddisabled | |
last_via | string | Процесс, ПОСЛЕДНИМ ИЗМЕНИВШИЙ её. open_apisimulateseed | |
via | string | Процесс, СОЗДАВШИЙ тестовую eSIM. open_apisimulateseed | |
smdp_status | string | допускает null | — |
status | string | — | |
updated_at | string · date-time | — | |
usage | object | допускает null | Статистика использованных данных eSIM. |
expires_at | string · date-time | допускает null | — |
remaining_bytes | integer · int64 | допускает null | — |
source | string | допускает null | —pollwebhook |
total_bytes | integer · int64 | допускает null | — |
updated_at | string · date-time | допускает null | — |
used_bytes | integer · int64 | допускает null | — |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbiddeninternal.not_foundtestmode.invalid_scenariopartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"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
}Пример тела запроса
{
"data_used_mb": 4096,
"expire_at": "2026-07-11T08:12:30Z",
"expire_now": true,
"provider_reject": true,
"status": "enabled",
"throttled": true
}/v1/testmode/resetСбросить тестовые данные (только тестовые ключи)
orders:writepost_testmode_resetТребуемая область доступа: orders:write.
Заголовки (1)
Idempotency-KeystringнеобязательноТело запросанеобязательноapplication/json
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
reason | string | — |
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
esim_states_deleted | integer · int64 | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
order_items_deleted | integer · int64 | — | |
orders_deleted | integer · int64 | — | |
wallet_balance | integer · int64 | Баланс тестового кошелька ПОСЛЕ сброса, всегда 0. Реестр НЕ удаляется: обнуление выполняется компенсирующей проводкой. | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbiddenpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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"Пример ответа
{
"data": {
"esim_states_deleted": 1,
"mode": "live",
"order_items_deleted": 1,
"orders_deleted": 1,
"wallet_balance": 1
},
"ok": true
}Пример тела запроса
{
"reason": "Reset test data"
}/v1/testmode/seedСоздать примеры тестовых данных (только тестовые ключи)
orders:writepost_testmode_seedТребуемая область доступа: orders:write.
Заголовки (1)
Idempotency-KeystringнеобязательноОтвет
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
already_seeded | boolean | — | |
esims_created | integer | — | |
mode | string | Среда вызывающего ключа, соответствующая заголовку X-TKE-Mode. Организации используют tke_live_/tke_test_, участники — tke_member_live_/tke_member_test_. Рабочий и тестовый хосты проверяют режим ключа. testlive | |
orders_created | integer | — | |
seeded | boolean | — | |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbiddentestmode.no_sellable_packagepartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-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)"Пример ответа
{
"data": {
"already_seeded": true,
"esims_created": 1,
"mode": "live",
"orders_created": 1,
"seeded": true
},
"ok": true
}/v1/testmode/statusОбзор тестового режима (только тестовые ключи)
esims:readget_testmode_statusТребуемая область доступа: esims:read.
Ответ
Успешно
| Поле | Тип | Ограничения | Описание |
|---|---|---|---|
data | object | обязательно | — |
base_url_testing | string | Хост тестового режима. Используйте только ключи tke_test_ или tke_member_test_; обращение к рабочему хосту возвращает 403 partner.wrong_host. | |
capabilities | object | — | |
bulk_orders | boolean | — | |
cancel_refund | boolean | — | |
refund_requests | boolean | — | |
webhooks | boolean | — | |
checklist | object[] | Этапы интеграции определяются по реальным вызовам API того же владельца в тестовом режиме. Проверка вебхуков применяется только к организациям. | |
done | boolean | — | |
label | string | — | |
last_at | string · date-time | допускает null | — |
step | string | — | |
drift | object | Цикл имитации расхода: включён ли он, интервал и последний запуск. Вебхуки порогов отправляются только организациям с поддержкой вебхуков. | |
enabled | boolean | — | |
interval_secs | integer · int64 | — | |
last_run_at | string · date-time | допускает null | — |
mode | string | —testlive | |
ready | boolean | Выполнены ли владельцем ключа все поддерживаемые пункты проверки. Участникам не требуется проверка вебхуков. | |
sample_counts | object | — | |
test_esims | integer · int64 | — | |
test_orders | integer · int64 | — | |
seeded | boolean | Есть ли у владельца ключа образцы тестовых заказов и eSIM. | |
test_key_count | integer · int64 | — | |
wallet_balance | integer · int64 | Баланс тестового кошелька владельца ключа в виртуальных VND, изолированный от рабочего кошелька. | |
webhook | object | — | |
endpoint_url | string | допускает null | — |
fail_next | integer | — | |
status | string | допускает null | — |
ok | boolean | обязательно | —true |
Ответы с ошибкой
Все ошибки возвращаются в формате { ok: false, error } с соответствующим HTTP-статусом, никогда с 200. Описания кодов есть на странице ошибок.
internal.validation_failedpartner.invalid_keypartner.invalid_signaturepartner.timestamp_expiredpartner.nonce_reusedpartner.ip_not_allowedpartner.scope_deniedpartner.key_suspendedpartner.key_revokedorg.suspendedtestmode.live_key_forbiddenpartner.rate_limitedpartner.endpoint_disabledinternal.service_unavailableПример вызова
Все значения TKE-VN-30D-X3K9F нужно заменить своими. Реальные коды тарифов получите через GET /v1/catalog/packages.
#!/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: ru" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG"Пример ответа
{
"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
}