API
Для разработчиков · API v1

Документация API

Покупайте ключи, подарочные карты и пополнения со своего баланса прямо из магазина или бота: каталог с вашими ценами, заказы, коды, баланс и вебхуки — по REST и JSON.

Базовый адрес https://packetuphub.com/v1
  • Подпись HMAC-SHA256
  • Идемпотентные заказы
  • Вебхуки с повторами
  • OpenAPI 3.0

Четыре шага от ключа до первого кода. Всё общение — HTTPS-запросы с JSON к адресу https://packetuphub.com/v1.

  1. 1
    Выпустите ключ

    В кабинете, «Разработчикам → API-ключи»: выберите права и, если нужно, IP-адреса. Секрет sk_… показывается один раз — сохраните его в своём хранилище секретов.

  2. 2
    Подпишите запрос

    Добавьте заголовки X-Api-Key, X-Timestamp и X-Signature (как считать подпись) и проверьте их на GET /v1/balance.

  3. 3
    Оформите заказ

    POST /v1/orders с sku из GET /v1/products и уникальным Idempotency-Key. Стоимость резервируется на балансе, заказ сразу в статусе processing.

  4. 4
    Заберите коды

    Дождитесь вебхука order.completed (или опрашивайте заказ) и получите коды из items в GET /v1/orders/{id}.

Аутентификация

Подпись запросов

Каждый запрос подписывается секретом ключа. Секрет по сети не передаётся: сервер считает ту же подпись и сравнивает.

ЗаголовокЗначение
X-Api-KeyId ключа, kh_…
X-TimestampТекущее Unix-время в секундах. Отличается от нашего больше чем на 5 минут в любую сторону — запрос отклоняется
X-SignatureHMAC-SHA256 строки для подписи с ключом-секретом sk_…, в нижнем регистре hex (64 символа)

Строка для подписи

Строки, соединённые переводом строки \n, без перевода строки в конце. Пятая — только у запросов с Idempotency-Key (создание заказа):

  1. 1793088000значение X-Timestamp
  2. POSTHTTP-метод заглавными буквами
  3. /v1/ordersпуть с query-строкой, без хоста: /v1/orders?limit=10
  4. df7c6ce8062ff49c5906ea4d91ae0118afb4f6225c87933ea48bc7a07dc5d94fSHA-256 тела запроса, hex в нижнем регистре
  5. shop-58213значение Idempotency-Key; без этого заголовка строки нет
  • Путь — ровно как его отправляет ваш HTTP-клиент, вместе с ? и параметрами, в том же порядке и кодировке.
  • Тело хешируется в тех байтах, что уходят в запрос: сериализуйте JSON один раз и отправляйте ту же строку. У GET и DELETE тела нет — это хеш пустой строки: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
  • Ключ HMAC — весь секрет, вместе с префиксом sk_.
  • Idempotency-Key подписывается, чтобы перехваченный запрос нельзя было отправить ещё раз как новый заказ с другим ключом.
  • Часы сервера держите синхронизированными (NTP): окно — ±5 минут.
  • Нет заголовка, время вне окна, ключ неизвестен или отозван, подпись не совпала — ответ 401 unauthorized.

Пример

API_KEY="kh_…"   # id ключа
SECRET="sk_…"    # секрет ключа, показан один раз при создании
BASE="https://packetuphub.com"

METHOD="POST"
URI="/v1/orders"  # путь с query-строкой, без хоста
BODY='{"sku":"pubg-660","quantity":1,"uid":"5123456789"}'  # для GET — пустая строка
IDEM="shop-58213"  # ваш номер заказа; для GET — пустая строка

TS=$(date +%s)
HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 | awk '{print $NF}')
TO_SIGN=$(printf '%s\n%s\n%s\n%s' "$TS" "$METHOD" "$URI" "$HASH")
[ -n "$IDEM" ] && TO_SIGN=$(printf '%s\n%s' "$TO_SIGN" "$IDEM")
SIG=$(printf '%s' "$TO_SIGN" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')

# для GET уберите Idempotency-Key, Content-Type и --data-raw
curl -sS -X "$METHOD" "$BASE$URI" \
  -H "X-Api-Key: $API_KEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: $SIG" \
  -H "Idempotency-Key: $IDEM" \
  -H "Content-Type: application/json" \
  --data-raw "$BODY"
Проверьте свою реализацию

С этими входными данными подпись должна получиться ровно такой — её посчитал код сервера.

Секрет
sk_example
X-Timestamp
1793088000
Запрос
POST /v1/orders
Idempotency-Key
shop-58213
Тело
{"sku":"pubg-660","quantity":1,"uid":"5123456789"}
SHA-256 тела
df7c6ce8062ff49c5906ea4d91ae0118afb4f6225c87933ea48bc7a07dc5d94f
X-Signature
d2c0d0811a93df7401fa5a02a7e2817450ccaeeb43cc68c04b79c25491f1dc56
Аутентификация

Права и IP-адреса

Ключ работает только для того, на что выпущен. Права и адреса задаются при создании ключа в кабинете; чтобы их поменять, выпустите новый ключ и отзовите старый.

ПравоМетоды
catalog:readGET /v1/products
orders:writePOST /v1/orders
orders:readGET /v1/orders, GET /v1/orders/{id}
balance:readGET /v1/balance
webhooks:write/v1/webhook и всё под ним
  • Без нужного права — 403 forbidden.
  • IP-адреса. Ключ можно ограничить списком до 50 адресов или сетей (IPv4 и IPv6, CIDR: 203.0.113.0/24). С другого адреса — 403 ip_not_allowed. Пустой список — любой адрес.
  • Ключи выпускают и отзывают владелец, администратор и разработчик компании; оба действия попадают в журнал безопасности. Отозванный ключ перестаёт работать сразу.
Надёжность

Идемпотентность

Сеть может оборваться после того, как заказ создан, но до того, как вы получили ответ. Чтобы повтор не купил второй раз, у каждого заказа есть ваш ключ — заголовок Idempotency-Key.

  • Обязателен в POST /v1/orders: 1–100 символов, пробелы по краям отбрасываются. Без заголовка — 400 invalid_request.
  • Это и ваш номер заказа: в кабинете, выгрузках CSV, ответах и вебхуках он виден как reference. Удобно передавать номер заказа из своей системы.
  • Повтор с тем же ключом и теми же sku, quantity, uid и zone отвечает 201 с исходным заказом в его текущем состоянии — деньги второй раз не резервируются.
  • Тот же ключ с другими параметрами — 409 idempotency_conflict.
  • Ключ уникален в пределах вашей компании и не истекает.
При таймауте или ошибке сети повторяйте POST /v1/orders с тем же ключом, а не с новым.
JSON

Тела запросов и ответов — JSON в UTF-8, Content-Type: application/json. Тело запроса — до 1 МиБ.

Деньги

Доллары США строкой с 2–6 знаками после точки: "10.50". Не переводите их в float — считайте в десятичных.

Время

RFC 3339 в UTC: 2026-10-28T12:00:42Z, секунды могут быть с дробной частью.

Идентификаторы

UUID: 0f8a5c2e-9b1d-4e7a-a3f6-5d2c8e1b7a40. У заказа есть и короткий number для людей.

Необязательные поля

Если значения нет, поля в ответе нет совсем, а не null.

Ошибки

application/problem+json по RFC 9457 со стабильным полем code — см. Ошибки.

Справочник

Методы

Кроме ошибок, указанных у метода, любой запрос может получить unauthorized , ip_not_allowed , forbidden , rate_limited , internal_error .

Баланс

GET/v1/balance balance:read

Ваш баланс

Доступная сумма и сумма, зарезервированная под заказы, которые ещё выполняются.

Ответ 200 OK Balance
Пример ответа
{
  "available": "1250.00",
  "held": "45.80",
  "currency": "USD"
}

Каталог

GET/v1/products catalog:read

Всё, что продаётся: по позиции на номинал, с вашими ценами

Весь каталог одним ответом, без страниц. Цена — уже с учётом скидки вашего уровня: по ней будет списан заказ, оформленный сейчас.

Ответ 200 OK Product
Пример ответа
{
  "items": [
    {
      "sku": "pubg-660",
      "name": "PUBG Mobile UC · 660 UC",
      "product": "PUBG Mobile UC",
      "denomination": "660 UC",
      "category": "topup",
      "region": "GLOBAL",
      "kind": "topup",
      "price": "8.90",
      "max_quantity": 10,
      "available": true
    },
    {
      "sku": "steam-us-20",
      "name": "Steam Wallet · 20 USD",
      "product": "Steam Wallet",
      "denomination": "20 USD",
      "category": "gift",
      "region": "US",
      "kind": "code",
      "price": "19.40",
      "max_quantity": 50,
      "available": true
    }
  ]
}

Заказы

POST/v1/orders orders:write

Оформить заказ

Резервирует стоимость на балансе и сразу отвечает заказом в статусе processing; единицы покупаются по одной. Итог придёт вебхуком или виден в GET /v1/orders/{id}. Повтор с тем же Idempotency-Key возвращает исходный заказ в его текущем состоянии.

ПараметрТипОписание
Idempotency-Key header обязательныйstringВаш уникальный id заказа, 1–100 символов. Он же ваш номер заказа: в кабинете, CSV и вебхуках виден как reference.
sku body обязательныйstringПозиция из GET /v1/products.
quantity body обязательныйintegerОт 1 до max_quantity товара (не больше 500).
uid body stringID аккаунта игрока. Обязателен для товаров kind: topup, для kind: code передавать нельзя.
zone body stringВторая часть аккаунта (зона или сервер). Обязательна, если у товара есть zone_label, иначе передавать нельзя.
Ответ 201 Created Order
Пример ответа
{
  "id": "0f8a5c2e-9b1d-4e7a-a3f6-5d2c8e1b7a40",
  "number": 10482,
  "reference": "shop-58213",
  "sku": "pubg-660",
  "product_name": "PUBG Mobile UC · 660 UC",
  "quantity": 1,
  "unit_price": "8.90",
  "total": "8.90",
  "uid": "5123456789",
  "status": "processing",
  "created_at": "2026-10-28T12:00:31Z",
  "items": [
    { "id": "6b1f0c9e-2a47-4d3b-9e85-3c7a1d2f4e60", "status": "processing" }
  ]
}
GET/v1/orders orders:read

Ваши заказы, новые сверху, без кодов

Заказы из API и из кабинета. Следующая страница — с before равным next_before; на последней странице next_before нет.

ПараметрТипОписание
limit query integerСколько заказов на странице: 1–100, по умолчанию 50.
before query uuidЗаказы, оформленные раньше этого: next_before предыдущей страницы.
Ответ 200 OK Order
Пример ответа
{
  "items": [
    {
      "id": "0f8a5c2e-9b1d-4e7a-a3f6-5d2c8e1b7a40",
      "number": 10482,
      "reference": "shop-58213",
      "sku": "pubg-660",
      "product_name": "PUBG Mobile UC · 660 UC",
      "quantity": 1,
      "unit_price": "8.90",
      "total": "8.90",
      "uid": "5123456789",
      "status": "completed",
      "created_at": "2026-10-28T12:00:31Z",
      "completed_at": "2026-10-28T12:00:42Z"
    }
  ],
  "next_before": "0f8a5c2e-9b1d-4e7a-a3f6-5d2c8e1b7a40"
}
Ошибки invalid_request
GET/v1/orders/{id} orders:read

Заказ с выданными кодами

Отсюда забирают коды: у выданных единиц в items есть code (и pin, если он есть у карты), у пополнений — operation. Чужой заказ отвечает 404, как несуществующий.

ПараметрТипОписание
id path обязательныйuuidid заказа из ответа на создание, списка или вебхука.
Ответ 200 OK Order
Пример ответа
{
  "id": "3c9e7a10-5b2d-4f8e-a1c6-9d0b2e4f6a81",
  "number": 10483,
  "reference": "shop-58214",
  "sku": "steam-us-20",
  "product_name": "Steam Wallet · 20 USD",
  "quantity": 2,
  "unit_price": "19.40",
  "total": "38.80",
  "status": "completed",
  "created_at": "2026-10-28T12:03:10Z",
  "completed_at": "2026-10-28T12:03:14Z",
  "items": [
    {
      "id": "a4d2c6e8-1f3b-4a5c-8e7d-9b0f1a2c3d4e",
      "status": "delivered",
      "code": "7KXQ2-M9PLA-3RTZB",
      "operation": "SUP-884120"
    },
    {
      "id": "b5e3d7f9-2a4c-4b6d-9f8e-0c1a2b3d4e5f",
      "status": "delivered",
      "code": "4HWNC-Q8DTE-6YJPU",
      "operation": "SUP-884121"
    }
  ]
}

Вебхук

GET/v1/webhook webhooks:write

Настройки вебхука

Адрес и события. Секрета в ответе нет: он приходит один раз — при создании.

Ответ 200 OK Webhook
Пример ответа
{
  "url": "https://shop.example.com/packetuphub/webhook",
  "events": ["order.completed", "order.failed", "order.partially_completed"],
  "created_at": "2026-10-20T09:15:00Z",
  "updated_at": "2026-10-27T18:02:11Z"
}
PUT/v1/webhook webhooks:write

Создать или изменить вебхук

У компании один адрес. Ответ на создание содержит секрет подписи secret (whsec_…) — сохраните его; при изменении секрет остаётся прежним и в ответ не попадает.

ПараметрТипОписание
url body обязательныйstringАбсолютный адрес https:// без логина и пароля, до 2000 символов. Должен вести на публичный IP-адрес; редиректы не выполняются.
events body обязательныйstring[]Хотя бы одно событие из списка в разделе «Вебхуки». ping указывать не нужно — он приходит всегда.
Ответ 200 OK Webhook
Пример ответа
{
  "url": "https://shop.example.com/packetuphub/webhook",
  "events": ["order.completed", "order.failed", "order.partially_completed"],
  "secret": "whsec_…",
  "created_at": "2026-10-28T12:10:00Z",
  "updated_at": "2026-10-28T12:10:00Z"
}
Ошибки invalid_request
DELETE/v1/webhook webhooks:write

Удалить вебхук

События, которые ещё ждут отправки, не будут доставлены.

Ответ 204 No Content без тела
POST/v1/webhook/ping webhooks:write

Отправить тестовое событие ping

Ставит в очередь событие ping с пустым data — так проверяют адрес и проверку подписи на своей стороне.

Ответ 202 Accepted без тела
GET/v1/webhook/deliveries webhooks:write

Последние отправленные события, новые сверху

Журнал за 30 дней: статус доставки, число попыток и ответ вашего сервера на последнюю.

ПараметрТипОписание
limit query integerСколько событий вернуть: 1–100, по умолчанию 50.
Ответ 200 OK WebhookDelivery
Пример ответа
{
  "items": [
    {
      "id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
      "type": "order.completed",
      "status": "delivered",
      "attempts": 1,
      "last_status_code": 200,
      "created_at": "2026-10-28T12:00:42Z",
      "delivered_at": "2026-10-28T12:00:43Z"
    },
    {
      "id": "019a2b30-11aa-7c3e-9d2f-5e6a7b8c9d0e",
      "type": "balance.credited",
      "status": "pending",
      "attempts": 2,
      "last_status_code": 503,
      "last_error": "HTTP 503 Service Unavailable",
      "created_at": "2026-10-28T11:58:02Z"
    }
  ]
}
Ошибки invalid_request
Справочник

Объекты

Поля ответов. Money — строка с суммой в долларах, см. Форматы данных.

Balance Ответ GET /v1/balance.
ПолеТипОписание
available обязательныйMoneyДоступно для новых заказов.
held обязательныйMoneyЗарезервировано под заказы, которые ещё выполняются.
currency обязательныйstringВсегда USD.
Product Элемент items в GET /v1/products: один номинал товара.
ПолеТипОписание
sku обязательныйstringКод позиции, его передают в заказе.
name обязательныйstringТовар и номинал одной строкой: PUBG Mobile UC · 660 UC.
product обязательныйstringНазвание товара.
denomination обязательныйstringНоминал.
category обязательныйstringРаздел каталога: gift, keys или topup.
region обязательныйstringРегион товара, например GLOBAL.
kind обязательныйstringcode — вы получаете код; topup — пополняется аккаунт игрока, переданный в uid.
zone_label stringЕсть у пополнений, где аккаунт игрока состоит из двух частей (зона или сервер): передайте вторую часть в zone.
price обязательныйMoneyВаша цена: каталожная минус скидка вашего уровня. Меняется вместе с уровнем.
max_quantity обязательныйintegerСколько единиц можно взять одним заказом (у пополнений — предел пакетов за одну покупку у поставщика).
available обязательныйbooleanfalse, пока ни один поставщик не может продать товар; заказы тогда отклоняются.
Order Заказ: ответ на создание, GET /v1/orders/{id} и элемент items в списке заказов.
ПолеТипОписание
id обязательныйuuidИдентификатор заказа в API.
number обязательныйintegerКороткий номер, который показывает кабинет.
reference обязательныйstringВаш номер заказа — Idempotency-Key запроса; пустой у заказов из кабинета без номера.
sku обязательныйstringПозиция каталога.
product_name обязательныйstringТовар и номинал.
quantity обязательныйintegerСколько единиц заказано.
unit_price обязательныйMoneyЦена единицы на момент заказа.
total обязательныйMoneyunit_price × quantity. Невыданные единицы возвращаются на баланс.
uid stringАккаунт игрока, у пополнений.
zone stringВторая часть аккаунта, если она есть.
status обязательныйstringprocessing, completed, partial или failed — см. «Статусы заказа».
created_at обязательныйdate-timeКогда заказ оформлен.
completed_at date-timeКогда заказ завершён; нет, пока он выполняется.
items OrderItem[]Единицы заказа. Есть в ответе на создание и в GET /v1/orders/{id}, в списке заказов их нет.
OrderItem Одна единица заказа.
ПолеТипОписание
id обязательныйuuidИдентификатор единицы; его же называет событие order.review.
status обязательныйstringprocessing, delivered или failed. Стоимость failed возвращена на баланс.
code stringКод товара — у выданных единиц товаров kind: code.
pin stringPIN — у подарочных карт, где он есть.
operation stringНомер покупки у поставщика, после выдачи. По нему поддержка игры находит зачисление пополнения.
Webhook Настройки вебхука.
ПолеТипОписание
url обязательныйstringКуда отправляются события.
events обязательныйstring[]На какие события вы подписаны.
secret stringСекрет подписи whsec_…; только в ответе, который создал вебхук.
created_at обязательныйdate-timeКогда вебхук создан.
updated_at обязательныйdate-timeКогда изменён в последний раз.
WebhookDelivery Элемент items в GET /v1/webhook/deliveries.
ПолеТипОписание
id обязательныйuuidId события; приходит в заголовке X-Webhook-Id.
type обязательныйstringТип события.
status обязательныйstringpending — ждёт отправки или повтора, delivered — ваш сервер ответил 2xx, failed — попытки кончились.
attempts обязательныйintegerСколько раз событие отправлялось.
last_status_code integerHTTP-статус последней попытки; нет, если ответа не было.
last_error stringПочему последняя попытка не удалась.
created_at обязательныйdate-timeКогда событие произошло.
delivered_at date-timeКогда доставлено.

Заказ создаётся в processing и один раз переходит в итоговый статус. Каждому итогу соответствует своё событие вебхука.

processing

Стоимость зарезервирована на балансе (held), единицы покупаются у поставщика по одной.

completed

Выданы все единицы.

order.completed
partial

Часть выдана, стоимость остальных вернулась на баланс.

order.partially_completed
failed

Ничего не выдано, вся сумма вернулась на баланс.

order.failed
order.review

Пока заказ в processing, исход покупки одной из единиц может оказаться неизвестен. Тогда приходит событие order.review: мы выясняем результат у поставщика, деньги остаются зарезервированными, заказ остаётся в processing и затем получает итоговый статус как обычно.

Единицы заказа

У каждой единицы в items свой статус: processing → delivered (есть code или operation) или failed (стоимость возвращена). Пополнения по ID выполняются строго по очереди, коды — по одному.

Уведомления

Вебхуки

Вместо опроса заказов укажите HTTPS-адрес через PUT /v1/webhook или в кабинете («Разработчикам → Вебхуки»). Когда что-то происходит, мы отправляем на него POST с JSON-событием.

СобытиеКогдаdata
order.completedВыданы все единицы заказа
{ "order_id": "…", "number": 10482, "reference": "shop-58213", "status": "completed" }
order.partially_completedЧасть выдана, остальное возвращено
order.failedНичего не выдано, всё возвращено
order.reviewИсход покупки единицы неизвестен, мы разбираемся; деньги зарезервированы
{ "order_id": "…", "number": 10482, "reference": "shop-58213", "item_id": "…" }
balance.creditedЗачислено пополнение баланса; reference — хеш транзакции
{ "amount": "500.00", "reference": "…" }
pingТестовое событие по запросу; приходит всегда, на него не подписываются
{}

У событий о заказах reference есть, только если у заказа был ваш номер. События «тонкие»: коды в них не попадают — заберите их через GET /v1/orders/{id}.

Запрос к вашему серверу

POST на ваш адрес
POST /packetuphub/webhook HTTP/1.1
Content-Type: application/json
X-Webhook-Id: 019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b
X-Webhook-Event: order.completed
X-Webhook-Timestamp: 1793088042
X-Webhook-Signature: 5d0c…9a1e

{
  "id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
  "type": "order.completed",
  "created_at": "2026-10-28T12:00:42Z",
  "data": {
    "order_id": "0f8a5c2e-9b1d-4e7a-a3f6-5d2c8e1b7a40",
    "number": 10482,
    "reference": "shop-58213",
    "status": "completed"
  }
}
ЗаголовокЗначение
X-Webhook-IdId события; при повторах тот же — по нему отбрасывайте дубликаты
X-Webhook-EventТип события
X-Webhook-TimestampUnix-время отправки в секундах; у каждой попытки своё
X-Webhook-SignatureHMAC-SHA256 строки {X-Webhook-Timestamp}\n{тело} с ключом-секретом вебхука whsec_…, hex в нижнем регистре

Проверка подписи

  • Считайте HMAC от сырых байтов тела, до разбора JSON.
  • Отклоняйте старые X-Webhook-Timestamp, например старше 5 минут: повторы подписываются заново со свежим временем, так что честный запрос всегда свежий.
  • Смена секрета. Секрет показывают и меняют в кабинете. Сутки после смены в X-Webhook-Signature две подписи через запятую — новым и старым секретом; принимайте запрос, если совпала любая.
import hashlib
import hmac
import time

from flask import Flask, abort, request

WEBHOOK_SECRET = b"whsec_…"
app = Flask(__name__)


@app.post("/packetuphub/webhook")
def webhook():
    ts = request.headers.get("X-Webhook-Timestamp", "")
    raw = request.get_data()  # байты тела ровно как пришли
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
        abort(400)
    want = hmac.new(WEBHOOK_SECRET, ts.encode() + b"\n" + raw, hashlib.sha256).hexdigest()
    sigs = request.headers.get("X-Webhook-Signature", "").split(",")
    if not any(hmac.compare_digest(want, s) for s in sigs):
        abort(401)
    event = request.get_json()
    # если event["id"] уже обработан — просто ответьте 2xx; иначе поставьте в очередь
    return "", 204
Проверьте свою реализацию
Секрет
whsec_example
X-Webhook-Timestamp
1793088042
Тело
{"id":"019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b","type":"order.completed","created_at":"2026-10-28T12:00:42Z","data":{"order_id":"0f8a5c2e-9b1d-4e7a-a3f6-5d2c8e1b7a40","number":10482,"reference":"shop-58213","status":"completed"}}
X-Webhook-Signature
75b3abbd2a60f111d98289f475000a1a00e9cb381a28938bdd48b9d12b14e980

Ответ и повторы

  • Ответьте любым 2xx в течение 10 секунд. Тяжёлую работу делайте после ответа, в очереди. Редиректы не выполняются: 3xx считается неудачей.
  • Иначе событие отправляется снова — всего до 8 попыток за ~22 часа:
  1. 1сразу
  2. 2+1 мин
  3. 3+5 мин
  4. 4+15 мин
  5. 5+1 ч
  6. 6+3 ч
  7. 7+6 ч
  8. 8+12 ч
  • После восьмой неудачи событие получает статус failed. Журнал доставок за 30 дней с запросом и ответом — в кабинете и в GET /v1/webhook/deliveries; из кабинета событие можно отправить повторно.
  • События могут приходить не по порядку, а одно и то же — больше одного раза. Итог заказа сверяйте с GET /v1/orders/{id}.
  • Вебхук можно выключить в кабинете: тогда события о заказах и балансе не отправляются, а ping — отправляется.
Справочник

Ошибки

Ошибки приходят как application/problem+json (RFC 9457). Решения принимайте по полю code — оно стабильно; title и detail — текст для людей и логов.

422 Unprocessable Entity
{
  "type": "about:blank",
  "title": "Quantity over limit",
  "status": 422,
  "code": "quantity_over_limit",
  "detail": "quantity must be between 1 and 10 for this product"
}
codeHTTPЧто случилось
invalid_request400 · 413 · 422Неверный JSON, параметр или заголовок, нет Idempotency-Key (400); тело больше 1 МиБ (413); Idempotency-Key из одних пробелов или длиннее 100 символов, неверный адрес или события вебхука (422).
unauthorized401Нет заголовков подписи, X-Timestamp дальше ±5 минут от нашего времени, ключ неизвестен или отозван, подпись не совпала.
forbidden403У ключа нет права на этот запрос.
ip_not_allowed403Запрос пришёл с адреса, которого нет в списке ключа.
company_frozen403Аккаунт заморожен на проверку: POST /v1/orders и GET /v1/orders/{id} недоступны до разморозки.
api_locked403API для компании ещё не открыт. Сколько осталось — на странице «API-ключи» в кабинете.
entry_fee_required402Пробный период закончился, а вход не оплачен: POST /v1/orders недоступен, пока вход не оплачен в кабинете.
insufficient_funds402Доступного баланса не хватает на заказ.
product_not_found404Нет позиции с таким sku.
product_unavailable409Товар сейчас не продаётся (available: false).
uid_required422Товар — пополнение: нужен uid.
uid_not_allowed422Товар — код: uid и zone передавать нельзя.
zone_required422У игры аккаунт из двух частей: нужна zone.
zone_not_allowed422У игры нет зоны: zone передавать нельзя.
quantity_over_limit422quantity вне 1…max_quantity; предел товара — в detail.
idempotency_conflict409Idempotency-Key уже использован для заказа с другими sku, quantity, uid или zone.
webhook_not_configured404Вебхук ещё не создан.
not_found404Заказа нет (или он чужой), либо нет такого метода API.
rate_limited429Превышен лимит запросов ключа; повторите через Retry-After секунд.
internal_error500Сбой на нашей стороне. Повторите позже; если не проходит — напишите в поддержку.
Заморозка аккаунта. Мы можем заморозить аккаунт на время проверки средств. До разморозки POST /v1/orders и GET /v1/orders/{id} отвечают 403 company_frozen; уже оформленные заказы ждут и продолжатся после неё. Баланс и список заказов работают, пополнения зачисляются. Напишите в поддержку.
Нагрузка

Лимиты

ЧтоПределСверх предела
Запросы одного ключа20 в секунду, всплеск до 40429 rate_limited, Retry-After: 1
Запросы с одного IP-адреса (сайт и API вместе; IPv6 — на сеть /64)50 в секунду, всплеск до 200429 текстом, Retry-After: 1
Тело запроса1 МиБ413 invalid_request
Расхождение X-Timestamp±5 минут401 unauthorized
Единиц в заказеmax_quantity товара422 quantity_over_limit
Страница списка (limit)1–100, по умолчанию 50значение приводится к пределам
IP-адресов и сетей у ключа50—
Ответ вебхука10 секундповтор по расписанию

Получив 429, подождите Retry-After секунд. Не опрашивайте заказы в цикле без паузы — подпишитесь на вебхуки.

Помощь

Поддержка

Не получается подключиться?

Пришлите метод, путь, время запроса и ответ (code и detail). Секреты sk_… и whsec_… не присылайте никому, в том числе нам.

Ежедневно 10:00–23:00 МСК