Программный доступ к виртуальным номерам, заказам и балансу аккаунта.
Рекомендуется
⟩Начните с официальных SDK
Для новых интеграций используйте TypeScript/JavaScript или Python SDK. Оба SDK по умолчанию работают с публичным API /v2, сохраняют ключи идемпотентности при безопасных повторах, предоставляют типизированные ошибки и поддерживают согласованный жизненный цикл OTP.
Создайте заказ с product_id для точного стабильного слота тарифа либо с catalog_product_id, необязательным operator_id, min_price/max_price и ключом идемпотентности для безопасных повторов платных маршрутизируемых вызовов.
catalog_product_idmax_priceIdempotency-Key
02
Используйте OTP
Дождитесь OTP, отправьте его в целевом приложении, затем вызовите finish, чтобы закрыть заказ.
waitForOtpwait_for_otpfinish
03
Повторяйте только при необходимости
После resend дождитесь нового кода через afterCode в TypeScript или after_code в Python.
can_resendresend_available_at
import { SmscodeClient, OtpTimeoutError } from "@smscode/sdk";const client = new SmscodeClient({ token: process.env.SMSCODE_TOKEN! });let orderId: number | undefined;try { const created = await client.orders.create({ catalog_product_id: Number(process.env.SMSCODE_CATALOG_PRODUCT_ID), max_price: "0.50", quantity: 1, }); const order = created.orders[0]!; orderId = order.id; const first = await client.orders.waitForOtp(orderId, { timeoutMs: 120_000 }); console.log(first.otpCode); // Отправьте этот код в целевом приложении. await client.orders.finish(orderId);} catch (err) { if (err instanceof OtpTimeoutError && orderId !== undefined) { const current = await client.orders.get(orderId); if (current.can_cancel) await client.orders.cancel(orderId); } throw err;}
import osfrom smscode import OtpTimeoutError, SmscodeClientwith SmscodeClient(token=os.environ["SMSCODE_TOKEN"]) as client: created = client.orders.create( catalog_product_id=int(os.environ["SMSCODE_CATALOG_PRODUCT_ID"]), max_price="0.50", quantity=1, ) order = created.orders[0] order_id = int(order["id"]) try: first = client.orders.wait_for_otp(order_id, timeout_ms=120_000) print(first.otp_code) # Отправьте этот код в целевом приложении. client.orders.finish(order_id) except OtpTimeoutError: current = client.orders.get(order_id) if current["can_cancel"]: client.orders.cancel(order_id) raise
Используйте can_resend и resend_available_at для тайминга resend. Низкоуровневые timestamps resend являются внутренними и не являются публичными полями ответа.
⟩Обзор
Все денежные поля в API /v1 указаны в IDR (индонезийская рупия), целыми числами — например, "price": 15000 и "balance": 500000 означают 15 000 ₨ и 500 000 ₨. Чтобы получить USD-проекцию того же реестра, переключитесь на API v2 с помощью переключателя версий выше.
⟩Аутентификация
Все API-запросы требуют Bearer token. Сгенерируйте его в разделе Настройки аккаунта в личном кабинете и включайте в каждый запрос:
Authorization:Bearer YOUR_API_TOKEN
Запросы без валидного токена получат ответ 401 UNAUTHORIZED.
⟩Базовый URL
Все пути endpoint ниже указаны относительно:
https://api.smscode.gg/v1
⟩Формат ответов
Каждый ответ возвращает JSON в едином формате. Все ответы содержат заголовок x-request-id для отладки.
Все денежные поля в API /v1 указаны в IDR (индонезийская рупия), целыми числами — например, "price": 15000 и "balance": 500000 означают 15 000 ₨ и 500 000 ₨. Чтобы получить USD-проекцию того же реестра, переключитесь на API v2 с помощью переключателя версий выше.
Возвращает операторов, доступных для выбора для страны + сервиса. Если доступны и реальные операторы, и запас Any, ответ включает строку Any с operator_id null; если продуктов для конкретных операторов нет, список пуст.
Возвращает список заказов аутентифицированного пользователя, отсортированный по дате (новые первыми). Поддерживает фильтрацию по статусу и пагинацию через offset.
Query-параметры
Название
Тип
Обязательный
Описание
limit
integer
Нет
Макс. результатов (1-100, по умолчанию 20)
offset
integer
Нет
Количество пропускаемых результатов (по умолчанию 0)
status
string
Нет
Фильтр по статусу: ACTIVE, OTP_RECEIVED, COMPLETED, CANCELED, EXPIRED (регистронезависимый)
Создаёт новый заказ виртуального номера. Баланс списывается автоматически. Поддерживает опциональный заголовок Idempotency-Key для предотвращения дублирования заказов при повторных запросах.
Тело запроса
Название
Тип
Обязательный
Описание
product_id
integer
Нет
Стабильный ID продукта точного слота тарифа для прямого заказа. Укажите ЛИБО его, ЛИБО catalog_product_id, но не оба.
catalog_product_id
integer
Нет
Маршрутизируемый umbrella-ID страны+платформы. Сервер выбирает текущий подходящий тариф. Укажите его или product_id.
operator_id
integer
Нет
Необязательный ID оператора из /catalog/operators. Допустимо только с catalog_product_id; для Any не указывайте.
min_price
integer
Нет
Необязательный нижний предел цены. Целое число IDR. Допустимо только с catalog_product_id.
max_price
integer
Нет
Необязательный верхний предел цены. Целое число IDR. Допустимо только с catalog_product_id.
prefer_provider
string
Нет
Необязательный код провайдера, предпочтительный при равных предложениях.
policy
string
Нет
Необязательная политика маршрутизации, действует только с catalog_product_id. Значения: cheapest (по умолчанию) выбирает самое дешёвое исправное предложение; best_success сначала ранжирует предложения по недавней успешности доставки. best_success оценивает каждого провайдера по доле заказов, получивших OTP, за последние 30 завершённых дней, по полосам в 10%, и учитывает провайдера только при наличии не менее 20 заказов за этот период — провайдеры ниже этого порога или без истории считаются нейтральными, поэтому новые предложения никогда не остаются без шанса (по запросу; сигнал стартует с нейтрального значения). Если также задан prefer_provider, предпочитаемый провайдер всё равно идёт первым.
quantity
integer
Нет
Количество (1-100, по умолчанию 1)
Передайте заголовок Idempotency-Key для безопасного повторения запросов без создания дубликатов. Ключ может содержать буквы, цифры, дефис и подчёркивание (A-Z a-z 0-9 _ -), не более 128 символов; недопустимый ключ отклоняется с 422 VALIDATION_ERROR. Повтор с тем же ключом и тем же телом запроса возвращает исходный результат (включая failed_count при частичном успехе). Повтор, дошедший до провайдера, но завершившийся ошибкой, записывается и при повторе возвращает ту же ошибку — используйте НОВЫЙ ключ для новой попытки. Сбои без побочных эффектов (недостаточно средств, нет доступного предложения) освобождают ключ, поэтому вы можете пополнить баланс и повторить с тем же ключом. Повторное использование ключа с другим телом возвращает 422 IDEMPOTENCY_KEY_REUSED, а ещё выполняющийся запрос с этим ключом — 409 REQUEST_IN_PROGRESS. Поле failed_reason в ответах на create всегда null — оно заполняется только при опросе/в списке заказов.
Пример запроса
curl -s -X POST https://api.smscode.gg/v1/orders/create \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: unique-request-id-123" \ -d '{"product_id":142,"quantity":1}'# Or route by catalog_product_id — the server picks a current tier.# product_id is the stable exact tier-slot id. catalog_product_id is the# country+platform umbrella for routed ordering. Optional min_price/max_price# bound the tier; operator_id scopes to a carrier from /catalog/operators.# Pass EITHER product_id OR catalog_product_id.curl -s -X POST https://api.smscode.gg/v1/orders/create \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: unique-request-id-124" \ -d '{"catalog_product_id":87,"min_price":12000,"max_price":20000,"operator_id":433}'
Реактивирует завершённый номер — заказывает тот же номер повторно для нового кода подтверждения, не арендуя новый. Подходит только завершённый заказ, номер которого поддерживает реактивацию (проверьте can_reactivate у заказа или получите предпросмотр через reactivate-options). Реактивированный дочерний заказ — это НОВЫЙ заказ, возвращаемый в том же формате, что и create; баланс списывается автоматически.
Тело запроса
Название
Тип
Обязательный
Описание
id
integer
Да
Завершённый заказ для реактивации.
max_price
integer
Нет
Необязательный верхний предел стоимости. Целое число IDR. Реактивация отклоняется с 422 VALIDATION_ERROR, если текущая стоимость его превышает.
Как и create, это денежная операция — передайте заголовок Idempotency-Key для безопасного повтора (create и reactivate никогда не конфликтуют по одному ключу). Повторное использование ключа с другим телом возвращает 422 IDEMPOTENCY_KEY_REUSED, а ещё выполняющийся запрос с этим ключом — 409 REQUEST_IN_PROGRESS. Номер, который нельзя реактивировать, возвращает 409 CONFLICT; недостаточный баланс — 409 INSUFFICIENT_BALANCE.
Предпросмотр того, сколько будет стоить реактивация прямо сейчас. Только чтение — не расходует Idempotency-Key и ничего не создаёт. Возвращает стоимость как целое число IDR. Доступно только для завершённого заказа, номер которого поддерживает реактивацию.
Path-параметры
Название
Тип
Обязательный
Описание
id
integer
Да
ID заказа, для которого нужно рассчитать предварительную стоимость реактивации (параметр пути).
Обновить URL и/или секрет webhook. Секрет генерируется автоматически при первой установке URL. Отправьте пустую строку для очистки. URL должен использовать HTTPS.
Тело запроса
Название
Тип
Обязательный
Описание
webhook_url
string
Нет
HTTPS URL для получения webhook-событий (пустая строка для очистки)
webhook_secret
string
Нет
Общий секрет для подписи HMAC-SHA256 (генерируется автоматически, если не указан при первой настройке)
Настройте webhook URL для получения push-уведомлений в реальном времени о событиях заказов вместо периодического опроса. Рекомендуемый подход для бот-скриптов.
События
Событие
Триггер
order.otp_received
Получено новое SMS; распознанный код может быть null
order.completed
Заказ отмечен как завершённый (вручную или по истечении срока)
order.expired
Заказ истёк до получения SMS (баланс возвращён)
order.canceled
Заказ отменён пользователем (баланс возвращён)
Каждое новое SMS создаёт это событие. otp_code может быть null при наличии otp_message. Несколько SMS-событий могут прийти не по порядку; используйте sms_revision, чтобы игнорировать более старую агрегированную пару.
Проверяйте эту подпись на своём сервере для подтверждения подлинности запроса. Доставка — однократная, с таймаутом 3 секунды, без повторов.
⟩Лимиты запросов
API-запросы ограничены по частоте для каждой группы endpoint. Превышение лимита возвращает 429 Too Many Requests с заголовком Retry-After, указывающим время ожидания в секундах.
Ответы с ошибкой содержат один из следующих кодов в error.code:
Код
HTTP
Описание
UNAUTHORIZED
401
Отсутствует или недействителен API-токен
FORBIDDEN
403
Доступ запрещён
NOT_FOUND
404
Ресурс не найден (заказ, обменный курс и т.д.)
CONFLICT
409
Дублирующий запрос или конфликт ресурсов
INSUFFICIENT_BALANCE
409
Недостаточно средств на балансе
VALIDATION_ERROR
422
Параметры запроса не прошли валидацию
RATE_LIMIT_EXCEEDED
429
Слишком много запросов (проверьте заголовок Retry-After)
INTERNAL_ERROR
500
Внутренняя ошибка сервера
PROVIDER_ERROR
422
Вышестоящий SMS-провайдер отклонил запрос. При сбоях создания заказа ошибка может содержать details: cause_counts (заказы со старым product_id — сводка с группировкой по причине) или attempts (заказы с catalog_product_id — результаты по каждой попытке), используя значения ok, no_numbers, insufficient_balance, price_rejected, provider_unavailable, provider_account_balance, provider_error.
NO_OFFER_AVAILABLE
422
Нет активного предложения, подходящего под запрошенный продукт и правила (лимит цены, наличие).
CANCEL_TOO_EARLY
409
Заказ слишком новый для отмены — подождите 2 минуты
REQUEST_IN_PROGRESS
409
Запрос на создание с этим ключом идемпотентности ещё выполняется
IDEMPOTENCY_KEY_REUSED
422
Этот ключ идемпотентности уже использовался с другим телом запроса
SERVICE_UNAVAILABLE
503
Сервис временно недоступен (обслуживание)
⟩Обзор
Все денежные поля в API /v2 указаны в USD и возвращаются как денежный объект — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }. amount — это десятичная строка; canonical_amount — точное значение реестра в IDR (используйте его для сверки). Применённый курс USD/IDR rate раскрывается один раз на ответ в meta.fx. v2 — это USD-проекция во время рендеринга поверх того же реестра в IDR, что и v1, — она никогда не хранит и не проводит операции в USD.
Все денежные поля в API /v2 указаны в USD и возвращаются как денежный объект — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }. amount — это десятичная строка; canonical_amount — точное значение реестра в IDR (используйте его для сверки). Применённый курс USD/IDR rate раскрывается один раз на ответ в meta.fx. v2 — это USD-проекция во время рендеринга поверх того же реестра в IDR, что и v1, — она никогда не хранит и не проводит операции в USD.
Идентично v1 — меняется только базовый путь (/v1 → /v2).
GET/catalog/operators
Возвращает операторов, доступных для выбора для страны + сервиса. Если доступны и реальные операторы, и запас Any, ответ включает строку Any с operator_id null; если продуктов для конкретных операторов нет, список пуст.
v2: денежные поля представлены денежными объектами в USD, а ответ содержит единый meta.fx { pair, rate, rate_as_of }. rate — это целое число IDR за 1 USD, поэтому USD = canonical_amount / rate. Итоги используют 2 знака после запятой; цены и возвраты по позициям — 4. Строго положительная сумма никогда не округляется до 0.00. rate_as_of — это метка времени курса в формате RFC3339 (вид +00:00) либо null, если метка времени не зафиксирована.
Только v2: если пригодного курса USD/IDR нет, денежные endpoint'ы возвращают 503 FX_RATE_UNAVAILABLE с заголовком Retry-After вместо денежного тела ответа. v1 такого никогда не возвращает.
GET/catalog/exchange-rate
Возвращает текущий обменный курс USD/IDR, используемый для конвертации валют.
Параметры
Нет — v2 всегда возвращает USD/IDR; параметр ?pair из v1 игнорируется.
v2: возвращает { pair, rate, rate_as_of } (без base_currency/quote_currency, без обёртки meta — курс и есть данные). ?pair игнорируется — v2 всегда возвращает USD/IDR (v1 учитывает ?pair). Возвращает 503 FX_RATE_UNAVAILABLE, если пригодного курса нет.
v2: денежные поля представлены денежными объектами в USD, а ответ содержит единый meta.fx { pair, rate, rate_as_of }. rate — это целое число IDR за 1 USD, поэтому USD = canonical_amount / rate. Итоги используют 2 знака после запятой; цены и возвраты по позициям — 4. Строго положительная сумма никогда не округляется до 0.00. rate_as_of — это метка времени курса в формате RFC3339 (вид +00:00) либо null, если метка времени не зафиксирована.
Только v2: если пригодного курса USD/IDR нет, денежные endpoint'ы возвращают 503 FX_RATE_UNAVAILABLE с заголовком Retry-After вместо денежного тела ответа. v1 такого никогда не возвращает.
GET/orders
Возвращает список заказов аутентифицированного пользователя, отсортированный по дате (новые первыми). Поддерживает фильтрацию по статусу и пагинацию через offset.
Query-параметры
Название
Тип
Обязательный
Описание
limit
integer
Нет
Макс. результатов (1-100, по умолчанию 20)
offset
integer
Нет
Количество пропускаемых результатов (по умолчанию 0)
status
string
Нет
Фильтр по статусу: ACTIVE, OTP_RECEIVED, COMPLETED, CANCELED, EXPIRED (регистронезависимый)
v2: денежные поля представлены денежными объектами в USD, а ответ содержит единый meta.fx { pair, rate, rate_as_of }. rate — это целое число IDR за 1 USD, поэтому USD = canonical_amount / rate. Итоги используют 2 знака после запятой; цены и возвраты по позициям — 4. Строго положительная сумма никогда не округляется до 0.00. rate_as_of — это метка времени курса в формате RFC3339 (вид +00:00) либо null, если метка времени не зафиксирована.
Только v2: если пригодного курса USD/IDR нет, денежные endpoint'ы возвращают 503 FX_RATE_UNAVAILABLE с заголовком Retry-After вместо денежного тела ответа. v1 такого никогда не возвращает.
GET/orders/{id}
Возвращает один заказ по ID. Доступны только заказы аутентифицированного пользователя.
v2: денежные поля представлены денежными объектами в USD, а ответ содержит единый meta.fx { pair, rate, rate_as_of }. rate — это целое число IDR за 1 USD, поэтому USD = canonical_amount / rate. Итоги используют 2 знака после запятой; цены и возвраты по позициям — 4. Строго положительная сумма никогда не округляется до 0.00. rate_as_of — это метка времени курса в формате RFC3339 (вид +00:00) либо null, если метка времени не зафиксирована.
Только v2: если пригодного курса USD/IDR нет, денежные endpoint'ы возвращают 503 FX_RATE_UNAVAILABLE с заголовком Retry-After вместо денежного тела ответа. v1 такого никогда не возвращает.
GET/orders/active
Список всех активных заказов (ACTIVE + OTP_RECEIVED). Используйте для отслеживания статуса OTP.
v2: этот endpoint не является денежным — он не возвращает ни amount, ни meta.fx (та же структура, что и в v1, но в /v2).
POST/orders/create
Создаёт новый заказ виртуального номера. Баланс списывается автоматически. Поддерживает опциональный заголовок Idempotency-Key для предотвращения дублирования заказов при повторных запросах.
Тело запроса
Название
Тип
Обязательный
Описание
product_id
integer
Нет
Стабильный ID продукта точного слота тарифа для прямого заказа. Укажите ЛИБО его, ЛИБО catalog_product_id, но не оба.
catalog_product_id
integer
Нет
Маршрутизируемый umbrella-ID страны+платформы. Сервер выбирает текущий подходящий тариф. Укажите его или product_id.
operator_id
integer
Нет
Необязательный ID оператора из /catalog/operators. Допустимо только с catalog_product_id; для Any не указывайте.
min_price
string
Нет
Необязательный нижний предел цены. Десятичная строка USD (например, "0.30"). Допустимо только с catalog_product_id.
max_price
string
Нет
Необязательный верхний предел цены. Десятичная строка USD (например, "0.50"). Допустимо только с catalog_product_id.
prefer_provider
string
Нет
Необязательный код провайдера, предпочтительный при равных предложениях.
policy
string
Нет
Необязательная политика маршрутизации, действует только с catalog_product_id. Значения: cheapest (по умолчанию) выбирает самое дешёвое исправное предложение; best_success сначала ранжирует предложения по недавней успешности доставки. best_success оценивает каждого провайдера по доле заказов, получивших OTP, за последние 30 завершённых дней, по полосам в 10%, и учитывает провайдера только при наличии не менее 20 заказов за этот период — провайдеры ниже этого порога или без истории считаются нейтральными, поэтому новые предложения никогда не остаются без шанса (по запросу; сигнал стартует с нейтрального значения). Если также задан prefer_provider, предпочитаемый провайдер всё равно идёт первым.
quantity
integer
Нет
Количество (1-100, по умолчанию 1)
Передайте заголовок Idempotency-Key для безопасного повторения запросов без создания дубликатов. Ключ может содержать буквы, цифры, дефис и подчёркивание (A-Z a-z 0-9 _ -), не более 128 символов; недопустимый ключ отклоняется с 422 VALIDATION_ERROR. Повтор с тем же ключом и тем же телом запроса возвращает исходный результат (включая failed_count при частичном успехе). Повтор, дошедший до провайдера, но завершившийся ошибкой, записывается и при повторе возвращает ту же ошибку — используйте НОВЫЙ ключ для новой попытки. Сбои без побочных эффектов (недостаточно средств, нет доступного предложения) освобождают ключ, поэтому вы можете пополнить баланс и повторить с тем же ключом. Повторное использование ключа с другим телом возвращает 422 IDEMPOTENCY_KEY_REUSED, а ещё выполняющийся запрос с этим ключом — 409 REQUEST_IN_PROGRESS. Поле failed_reason в ответах на create всегда null — оно заполняется только при опросе/в списке заказов.
v2: денежные поля представлены денежными объектами в USD, а ответ содержит единый meta.fx { pair, rate, rate_as_of }. rate — это целое число IDR за 1 USD, поэтому USD = canonical_amount / rate. Итоги используют 2 знака после запятой; цены и возвраты по позициям — 4. Строго положительная сумма никогда не округляется до 0.00. rate_as_of — это метка времени курса в формате RFC3339 (вид +00:00) либо null, если метка времени не зафиксирована.
Только v2: если пригодного курса USD/IDR нет, денежные endpoint'ы возвращают 503 FX_RATE_UNAVAILABLE с заголовком Retry-After вместо денежного тела ответа. v1 такого никогда не возвращает.
POST/orders/cancel
Отмена активного заказа. Стоимость аренды возвращается на баланс аккаунта.
v2: денежные поля представлены денежными объектами в USD, а ответ содержит единый meta.fx { pair, rate, rate_as_of }. rate — это целое число IDR за 1 USD, поэтому USD = canonical_amount / rate. Итоги используют 2 знака после запятой; цены и возвраты по позициям — 4. Строго положительная сумма никогда не округляется до 0.00. rate_as_of — это метка времени курса в формате RFC3339 (вид +00:00) либо null, если метка времени не зафиксирована.
Только v2: если пригодного курса USD/IDR нет, денежные endpoint'ы возвращают 503 FX_RATE_UNAVAILABLE с заголовком Retry-After вместо денежного тела ответа. v1 такого никогда не возвращает.
POST/orders/finish
Отметить заказ как завершённый после получения OTP. Номер освобождается сразу, без ожидания истечения срока.
Идентично v1 — меняется только базовый путь (/v1 → /v2).
POST/orders/reactivate
Реактивирует завершённый номер — заказывает тот же номер повторно для нового кода подтверждения, не арендуя новый. Подходит только завершённый заказ, номер которого поддерживает реактивацию (проверьте can_reactivate у заказа или получите предпросмотр через reactivate-options). Реактивированный дочерний заказ — это НОВЫЙ заказ, возвращаемый в том же формате, что и create; баланс списывается автоматически.
Тело запроса
Название
Тип
Обязательный
Описание
id
integer
Да
Завершённый заказ для реактивации.
max_price
string
Нет
Необязательный верхний предел стоимости. Десятичная строка USD (например, "0.50"). Реактивация отклоняется с 422 VALIDATION_ERROR, если текущая стоимость его превышает.
Как и create, это денежная операция — передайте заголовок Idempotency-Key для безопасного повтора (create и reactivate никогда не конфликтуют по одному ключу). Повторное использование ключа с другим телом возвращает 422 IDEMPOTENCY_KEY_REUSED, а ещё выполняющийся запрос с этим ключом — 409 REQUEST_IN_PROGRESS. Номер, который нельзя реактивировать, возвращает 409 CONFLICT; недостаточный баланс — 409 INSUFFICIENT_BALANCE.
v2: денежные поля представлены денежными объектами в USD, а ответ содержит единый meta.fx { pair, rate, rate_as_of }. rate — это целое число IDR за 1 USD, поэтому USD = canonical_amount / rate. Итоги используют 2 знака после запятой; цены и возвраты по позициям — 4. Строго положительная сумма никогда не округляется до 0.00. rate_as_of — это метка времени курса в формате RFC3339 (вид +00:00) либо null, если метка времени не зафиксирована.
Только v2: если пригодного курса USD/IDR нет, денежные endpoint'ы возвращают 503 FX_RATE_UNAVAILABLE с заголовком Retry-After вместо денежного тела ответа. v1 такого никогда не возвращает.
GET/orders/{id}/reactivate-options
Предпросмотр того, сколько будет стоить реактивация прямо сейчас. Только чтение — не расходует Idempotency-Key и ничего не создаёт. Возвращает стоимость как денежный объект USD с квитанцией FX. Доступно только для завершённого заказа, номер которого поддерживает реактивацию.
Path-параметры
Название
Тип
Обязательный
Описание
id
integer
Да
ID заказа, для которого нужно рассчитать предварительную стоимость реактивации (параметр пути).
v2: денежные поля представлены денежными объектами в USD, а ответ содержит единый meta.fx { pair, rate, rate_as_of }. rate — это целое число IDR за 1 USD, поэтому USD = canonical_amount / rate. Итоги используют 2 знака после запятой; цены и возвраты по позициям — 4. Строго положительная сумма никогда не округляется до 0.00. rate_as_of — это метка времени курса в формате RFC3339 (вид +00:00) либо null, если метка времени не зафиксирована.
Только v2: если пригодного курса USD/IDR нет, денежные endpoint'ы возвращают 503 FX_RATE_UNAVAILABLE с заголовком Retry-After вместо денежного тела ответа. v1 такого никогда не возвращает.
Идентично v1 — меняется только базовый путь (/v1 → /v2).
PATCH/webhook
Обновить URL и/или секрет webhook. Секрет генерируется автоматически при первой установке URL. Отправьте пустую строку для очистки. URL должен использовать HTTPS.
Тело запроса
Название
Тип
Обязательный
Описание
webhook_url
string
Нет
HTTPS URL для получения webhook-событий (пустая строка для очистки)
webhook_secret
string
Нет
Общий секрет для подписи HMAC-SHA256 (генерируется автоматически, если не указан при первой настройке)
Идентично v1 — меняется только базовый путь (/v1 → /v2).
⟩Webhook-уведомления
Настройте webhook URL для получения push-уведомлений в реальном времени о событиях заказов вместо периодического опроса. Рекомендуемый подход для бот-скриптов.
События
Событие
Триггер
order.otp_received
Получено новое SMS; распознанный код может быть null
order.completed
Заказ отмечен как завершённый (вручную или по истечении срока)
order.expired
Заказ истёк до получения SMS (баланс возвращён)
order.canceled
Заказ отменён пользователем (баланс возвращён)
Каждое новое SMS создаёт это событие. otp_code может быть null при наличии otp_message. Несколько SMS-событий могут прийти не по порядку; используйте sms_revision, чтобы игнорировать более старую агрегированную пару.
Проверяйте эту подпись на своём сервере для подтверждения подлинности запроса. Доставка — однократная, с таймаутом 3 секунды, без повторов.
⟩Лимиты запросов
API-запросы ограничены по частоте для каждой группы endpoint. Превышение лимита возвращает 429 Too Many Requests с заголовком Retry-After, указывающим время ожидания в секундах.
Ответы с ошибкой содержат один из следующих кодов в error.code:
Код
HTTP
Описание
UNAUTHORIZED
401
Отсутствует или недействителен API-токен
FORBIDDEN
403
Доступ запрещён
NOT_FOUND
404
Ресурс не найден (заказ, обменный курс и т.д.)
CONFLICT
409
Дублирующий запрос или конфликт ресурсов
INSUFFICIENT_BALANCE
409
Недостаточно средств на балансе
VALIDATION_ERROR
422
Параметры запроса не прошли валидацию
RATE_LIMIT_EXCEEDED
429
Слишком много запросов (проверьте заголовок Retry-After)
INTERNAL_ERROR
500
Внутренняя ошибка сервера
PROVIDER_ERROR
422
Вышестоящий SMS-провайдер отклонил запрос. При сбоях создания заказа ошибка может содержать details: cause_counts (заказы со старым product_id — сводка с группировкой по причине) или attempts (заказы с catalog_product_id — результаты по каждой попытке), используя значения ok, no_numbers, insufficient_balance, price_rejected, provider_unavailable, provider_account_balance, provider_error.
NO_OFFER_AVAILABLE
422
Нет активного предложения, подходящего под запрошенный продукт и правила (лимит цены, наличие).
CANCEL_TOO_EARLY
409
Заказ слишком новый для отмены — подождите 2 минуты
REQUEST_IN_PROGRESS
409
Запрос на создание с этим ключом идемпотентности ещё выполняется
IDEMPOTENCY_KEY_REUSED
422
Этот ключ идемпотентности уже использовался с другим телом запроса
SERVICE_UNAVAILABLE
503
Сервис временно недоступен (обслуживание)
FX_RATE_UNAVAILABLE
503
Обменный курс USD/IDR недоступен (денежные endpoint'ы v2) — возвращается 503 с заголовком Retry-After.
v1 → v2
⟩Миграция с v1 на v2
v1 работает с IDR; v2 работает с USD. Обе версии сосуществуют постоянно — отключения не планируется. Выберите одну версию на интеграцию; не смешивайте базовые пути. v2 идентична v1, отличается лишь способом представления денег.
Аспект
v1 · IDR
v2 · USD
Денежные поля
Целое IDR, напр. 15000
Денежный объект { amount, currency, canonical_amount, canonical_currency }
meta.fx
Отсутствует
Обязателен в каждом денежном ответе
Валюта
IDR
USD (зашит жёстко)
FX_RATE_UNAVAILABLE
—
Новый 503 + Retry-After, когда пригодного курса нет
Точность
—
Итоги 2 знака, цены/возвраты 4 знака, округление вверх для положительных
product_id — стабильный ID слота тарифа SMSCode. Сохраняйте его, если хотите заказать именно этот тариф; цена и доступность могут меняться в той же строке. catalog_product_id — стабильный umbrella-идентификатор страны+платформы для маршрутизируемого заказа; используйте его с необязательными operator_id, min_price, max_price, prefer_provider и policy, когда хотите, чтобы сервер выбрал текущий подходящий тариф.
Разбирайте денежные поля как объекты — читайте amount как десятичную строку; currency равно "USD".
Для сверки реестра используйте canonical_amount (точный IDR); сумма в USD amount — это проекция во время рендеринга, а rate раскрывается один раз в meta.fx.
Обрабатывайте новый FX_RATE_UNAVAILABLE (503) — повторите запрос после Retry-After. v1 такого никогда не возвращает.