Skip to content
Translated page. The English version is the source of truth.

Вебхуки — уведомления о заказах ​

Зарегистрируйте HTTPS-эндпоинт для получения подписанного вебхука в момент, когда один из ваших заказов выполнен и подтвержден в сети. Вместо периодического опроса (polling) вы продолжаете свой процесс (например, выплачиваете USDT), как только поступает уведомление.

Доставляются три события:

СобытиеОтправляется, когда
delegation.confirmedАренда Energy (1h / 5m) подтверждена в сети
bandwidth.delegatedЗаказ Bandwidth выполнен
activation.confirmedАктивация адреса выполнена в сети

На этой странице описаны API управления (создание / получение списка / редактирование / ротация секрета / удаление ваших эндпоинтов) и формат вебхуков, которые мы вам отправляем.

ℹ️ Роли. Здесь вы управляете своими эндпоинтами. Доставка выполняется Netts асинхронно после подтверждения заказа — опрашивать вручную ничего не нужно. Отправляются только события об успешном выполнении; сбои и таймауты никогда не доставляются.

🔒 Каждый отправляемый нами хеш предварительно проверяется в сети. Вебхук отправляется только после того, как каждый содержащийся в нем хеш транзакции найден в блоке. Если хеш еще не попал в блок, доставка приостанавливается и перепроверяется каждые 30 секунд в течение до 5 минут; если он так и не появится, по этому заказу ничего не отправляется. Вы никогда не получите хеш, которого нет в сети.

Базовый URL эндпоинта ​

https://netts.io/apiv2/webhooks

Заголовки запроса ​

ЗаголовокОбязательныйОписание
Content-TypeДа (для POST/PATCH)application/json
X-API-KEYДаВаш API-ключ из панели управления Netts
X-Real-IPДаIP-адрес из вашего белого списка

Ваш user_id определяется по API-ключу — передавать его не требуется. Вы можете просматривать и изменять только свои собственные эндпоинты.


Основной и резервный эндпоинты ​

Вы можете зарегистрировать не более двух эндпоинтов, и каждый из них имеет свою role:

РольНазначение
primaryАдрес, на который доставляется каждый вебхук.
backupРезервный. Используется только тогда, когда доставка на primary завершается ошибкой после исчерпания всех попыток.

Один подтвержденный заказ порождает один вебхук. Это не веерная рассылка (fan-out): одно и то же событие никогда не отправляется на оба адреса одновременно. Эндпоинт backup существует для отказоустойчивости — если ваш основной хост недоступен или постоянно возвращает ответ, отличный от 2xx, доставка переключается на резервный вместо того, чтобы быть отмененной.

Первый созданный вами эндпоинт становится primary, второй — backup. Вы можете указать role явно или поменять их местами позже с помощью PATCH.

Почему не отдельный URL для каждого типа операций? Потому что тип события передается внутри тела запроса, в поле event. Один обработчик, одна проверка подписи, а новые типы событий начнут поступать без необходимости регистрировать что-либо заново.


Управление эндпоинтами ​

Создание — POST /apiv2/webhooks ​

Регистрирует новый эндпоинт и возвращает secret, отображаемый только один раз (сохраните его — им подписывается каждый получаемый вами вебхук).

json
// request body — role is optional
{
    "url": "https://your-server.example/netts/delegation-hook",
    "role": "primary"
}

Если опустить role, будет назначена первая свободная: сначала primary, затем backup.

json
// response 201
{
    "detail": {
        "code": 10000,
        "status": "created",
        "data": {
            "id": 1,
            "url": "https://your-server.example/netts/delegation-hook",
            "secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "role": "primary",
            "is_active": true,
            "created_at": "2026-01-01T00:00:00"
        }
    }
}

Требования к URL (проверяются при создании и при каждом изменении):

  • должен быть https;
  • должен разрешаться в публичный адрес — loopback, приватные адреса (RFC1918), link-local (включая 169.254.169.254) и другие немаршрутизируемые диапазоны отклоняются;
  • никаких учетных данных в URL (user:pass@…);
  • длина до 2048 символов.

При отклонении URL возвращается ошибка 400.

Вы можете иметь два эндпоинта — один primary и один backup. Попытка добавить третий вернет 409 (4090). Запрос уже занятой role вернет 409 (4091) — поменяйте роли местами с помощью PATCH или сначала удалите существующий.

bash
curl -X POST https://netts.io/apiv2/webhooks \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: your_api_key" \
  -H "X-Real-IP: your_whitelisted_ip" \
  -d '{"url": "https://your-server.example/netts/delegation-hook", "role": "primary"}'

Список — GET /apiv2/webhooks ​

Возвращает ваши эндпоинты (secret здесь никогда не возвращается).

json
{
    "detail": {
        "code": 10000,
        "status": "ok",
        "data": {
            "endpoints": [
                {
                    "id": 1,
                    "url": "https://your-server.example/netts/delegation-hook",
                    "role": "primary",
                    "is_active": true,
                    "created_at": "2026-01-01T00:00:00",
                    "updated_at": "2026-01-01T00:00:00"
                },
                {
                    "id": 2,
                    "url": "https://backup.example/netts/delegation-hook",
                    "role": "backup",
                    "is_active": true,
                    "created_at": "2026-01-01T00:00:00",
                    "updated_at": "2026-01-01T00:00:00"
                }
            ],
            "count": 2,
            "max_endpoints": 2,
            "roles": ["primary", "backup"]
        }
    }
}

Получение одного — GET /apiv2/webhooks/{id} ​

Такой же формат, как и элемент списка (без secret). Чужой или несуществующий id возвращает 404.

Редактирование — PATCH /apiv2/webhooks/{id} ​

Изменение url, is_active и/или role. Отправьте любое подмножество; пустое тело возвращает 422. Измененный url валидируется повторно (https / SSRF). Чужой или несуществующий id возвращает 404.

json
// request body (any subset)
{ "url": "https://your-server.example/netts/new-hook", "is_active": false }
json
// response 200
{
    "detail": {
        "code": 10000,
        "status": "updated",
        "data": {
            "id": 1,
            "url": "https://your-server.example/netts/new-hook",
            "role": "primary",
            "is_active": false,
            "created_at": "2026-01-01T00:00:00",
            "updated_at": "2026-01-01T00:00:01"
        }
    }
}

Повышение резервного эндпоинта. Отправка {"role": "primary"} на ваш резервный эндпоинт меняет местами обе роли в рамках одной транзакции — старый основной становится резервным. Вы никогда не останетесь без основного адреса, и отдельный вызов для другого эндпоинта не требуется.

bash
curl -X PATCH https://netts.io/apiv2/webhooks/2 \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: your_api_key" \
  -H "X-Real-IP: your_whitelisted_ip" \
  -d '{"role": "primary"}'

Установите is_active: false, чтобы приостановить доставку без удаления эндпоинта; true — чтобы возобновить. Приостановка вашего primary не повышает статус резервного — доставка по-прежнему направлена на основной. Поменяйте роли местами, если хотите, чтобы резервный принял нагрузку на себя.

Ротация секрета — POST /apiv2/webhooks/{id}/rotate-secret ​

Генерирует новый secret и возвращает его один раз. Новый секрет вступает в силу немедленно для последующих доставок — никаких дополнительных действий не требуется. У каждого эндпоинта свой собственный секрет: ротация секрета основного эндпоинта не изменяет секрет резервного.

json
// response 200
{
    "detail": {
        "code": 10000,
        "status": "rotated",
        "data": {
            "id": 1,
            "secret": "whsec_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
        }
    }
}

Удаление — DELETE /apiv2/webhooks/{id} ​

Безвозвратно удаляет эндпоинт и освобождает его роль. Возвращает 204 (без тела); чужой или несуществующий id возвращает 404.

bash
curl -X DELETE https://netts.io/apiv2/webhooks/1 \
  -H "X-API-KEY: your_api_key" -H "X-Real-IP: your_whitelisted_ip"

Отправляемые вебхуки ​

Когда один из ваших заказов выполнен, Netts отправляет запрос POST на ваш эндпоинт primary. Каждое тело запроса передается в формате application/json (UTF-8); адреса и хеши всегда передаются полными значениями.

Поля, общие для всех событий:

ПолеТипОписание
eventstringТип события — ключ маршрутизации для вашего обработчика
delivery_idintИдентификатор доставки — ключ дедупликации на вашей стороне. Также передается в заголовке X-Netts-Delivery.
order_idstringИдентификатор вашего заказа
order_typestring1h, 5m, bandwidth или activation
tx_hashesstring[]Все хеши транзакций операции, каждый подтвержден в сети
confirmed_atstringUTC ISO-8601

delegation.confirmed — аренда Energy ​

json
{
    "event": "delegation.confirmed",
    "delivery_id": 1,
    "order_id": "1Hxxxxxxxxxx",
    "order_type": "1h",
    "receive_address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "energy_amount": 65000,
    "tx_hash": "0000000000000000000000000000000000000000000000000000000000000000",
    "tx_hashes": ["0000000000000000000000000000000000000000000000000000000000000000"],
    "delegation_timestamp": 1700000000000,
    "confirmed_at": "2026-01-01T00:00:00Z"
}
ПолеТипОписание
order_typestring1h или 5m
receive_addressstringTRON-адрес, получивший Energy
energy_amountintКоличество делегированной Energy
tx_hashstringУстаревшее поле, сохранено для совместимости: эквивалентно tx_hashes[0]
delegation_timestampint?Необязательно — присутствует только при подтверждении через путь Mongo

В новых интеграциях отдавайте предпочтение tx_hashes — заказ теоретически может быть выполнен несколькими транзакциями. Поле tx_hash продолжит работать.

bandwidth.delegated — заказ Bandwidth ​

json
{
    "event": "bandwidth.delegated",
    "delivery_id": 2,
    "order_id": "B1Hxxxxxxxxxxxxx",
    "order_type": "bandwidth",
    "rental_label": "1h",
    "rental_seconds": 3600,
    "receive_address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "bandwidth_amount": 400,
    "fulfillment": "delegated",
    "tx_hashes": ["0000000000000000000000000000000000000000000000000000000000000000"],
    "confirmed_at": "2026-01-01T00:00:00Z"
}
ПолеТипОписание
rental_label / rental_secondsstring / intСрок аренды, например 1h / 3600
receive_addressstringTRON-адрес, получивший Bandwidth
bandwidth_amountintЕдиницы Bandwidth (нетто)
fulfillmentstringСпособ выполнения заказа — см. ниже

Значения fulfillment:

ЗначениеЗначениеtx_hashes
delegatedBandwidth делегирована из нашего пула1+ хешей
trx_sendВыполнено путем отправки TRX на адрес вместо делегирования1+ хешей
already_enoughНа адресе уже было достаточно свободной Bandwidth — в сети ничего не отправлялосьпусто

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

activation.confirmed — активация адреса ​

json
{
    "event": "activation.confirmed",
    "delivery_id": 3,
    "order_id": "123456",
    "order_type": "activation",
    "address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "activation_type": "ACC_CREATE",
    "source": "telegram_bot",
    "tx_hashes": ["0000000000000000000000000000000000000000000000000000000000000000"],
    "confirmed_at": "2026-01-01T00:00:00Z"
}
ПолеТипОписание
order_idstringИдентификатор заказа на активацию (числовая строка)
addressstringАктивированный TRON-адрес
activation_typestringACC_CREATE (AccountCreateContract) или DIRECT (перевод TRX)
sourcestringМаркер источника. Либо сервисный тег, либо идентификатор заказа Energy, для которого потребовалась активация

Доставляются только фактические активации. Если адрес оказался уже активным и транзакция не производилась, вебхук не отправляется вовсе.

Заказ Energy, для которого также потребовалась активация, генерирует два вебхука — один activation.confirmed и один delegation.confirmed. Это независимые события с отдельными значениями delivery_id; маршрутизируйте их по полю event.

Отправляемые нами заголовки:

ЗаголовокЗначение
X-Netts-EventТип события: delegation.confirmed, bandwidth.delegated или activation.confirmed
X-Netts-Deliverydelivery_id (дедупликация)
X-Netts-Timestampunix-время в секундах на момент отправки
X-Netts-Signaturesha256=<hex>, hex = HMAC_SHA256(secret, "<timestamp>." + raw_body)
User-Agentnetts-webhook/1.0

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

Подпись формируется по схеме Stripe (timestamp.body), вычисляясь от сырых байтов, которые мы отправляем. Пересчитайте ее с вашим secret, сравните с постоянным временем выполнения и отклоните запрос, если X-Netts-Timestamp выходит за пределы окна в ±5 минут (защита от повторов/replay attacks).

Подписывайте секретом того эндпоинта, который принял запрос: у основного и резервного эндпоинтов разные секреты. Если оба ваших адреса обрабатываются одним и тем же сервисом, выбирайте секрет по URL, на который пришел запрос.

python
import hmac, hashlib, time

def verify(raw_body: bytes, sig_header: str, ts_header: str, secret: str) -> bool:
    # freshness (anti-replay)
    if abs(time.time() - int(ts_header)) > 300:
        return False
    signed = f"{ts_header}.".encode() + raw_body
    expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig_header)

# Flask example:
# ok = verify(request.get_data(),
#             request.headers["X-Netts-Signature"],
#             request.headers["X-Netts-Timestamp"], SECRET)

Семантика доставки (важно — at-least-once) ​

Доставка работает по схеме at-least-once (как минимум один раз): потерянный ответ может привести к повторной попытке, поэтому вы можете получить одно и то же событие дважды. Поскольку бизнес-действие (выплата USDT) связано с денежными средствами:

  1. Дедупликация обязательна — обрабатывайте каждое событие идемпотентно по delivery_id (и/или order_id); повторные вызовы должны быть холостыми операциями (no-op).
  2. Проверяйте HMAC перед любыми финансовыми действиями — не доверяйте телу запроса до тех пор, пока подпись не совпадет и X-Netts-Timestamp не окажется актуальным.
  3. Возвращайте 2xx только после надежного сохранения события — иначе мы (обоснованно) повторим попытку доставки.

Отвечайте кодом 2xx для подтверждения; любой ответ, отличный от 2xx, или таймаут вызывает повторную попытку.

Порядок попыток:

  1. Повторные попытки направляются на ваш эндпоинт primary. Окно попыток зависит от типа заказа: для заказов 5m повторы выполняются в течение ~1 минуты, для всех остальных типов — ~10 минут.
  2. Если окно исчерпано и у вас зарегистрирован backup, доставка переключается на него, а цикл повторных попыток запускается заново — с подписью собственным секретом резервного эндпоинта.
  3. Только после того, как попытки обращения к резервному эндпоинту также исчерпаны, доставка помечается как неудавшаяся.

Один и тот же delivery_id сохраняется на протяжении всего процесса, поэтому сообщение, завершившееся сбоем на основном эндпоинте и успешно принятое на резервном, остается одним событием для вашей логики дедупликации.


Справочник кодов ошибок ​

КодОписаниеHTTP-статус
10000Успешно (created / ok / updated / rotated)200 / 201
-Удалено (без тела)204
4000Неверный / небезопасный URL вебхука (не https, приватный/loopback, содержит учетные данные, слишком длинный)400
-1Неверный API-ключ / IP отсутствует в белом списке401
-1Эндпоинт не найден (или не принадлежит вам)404
4090Достигнут лимит эндпоинтов (максимум 2: primary, backup)409
4091Запрошенная роль уже занята — измените через PATCH или удалите существующий эндпоинт409
4220Нечего обновлять (PATCH с пустым телом)422
5003Не удалось создать эндпоинт (повторите попытку)503

Лимиты запросов (Rate Limits) ​

Ограничено для каждого API-ключа (заголовок X-API-KEY):

ПериодЛимит
1 секунда5 запросов
1 минута150 запросов

Превышение лимита запросов (429) ​

json
{ "message": "API rate limit exceeded" }

Примечания ​

  • Секрет показывается только один раз — при создании и при ротации. Он никогда не возвращается методами GET/LIST. Потеряли? выполните ротацию, чтобы получить новый.
  • Два эндпоинта, без веерной рассылки: один primary и один backup. Каждый подтвержденный заказ порождает один вебхук, доставляемый на основной; резервный используется только в том случае, если попытки доставки на основной исчерпаны.
  • Смена URL без простоя: зарегистрируйте новый адрес как backup, проверьте его работу, затем выполните PATCH роли на primary — смена происходит атомарно.
  • Приостановка: PATCH … {"is_active": false} останавливает доставку без удаления эндпоинта.
  • Только успешные события: delegation.confirmed, bandwidth.delegated, activation.confirmed. Событий сбоя нет — неудачный или завершившийся по таймауту заказ не создает вебхуков.
  • Новые типы событий могут добавляться со временем. Маршрутизируйте по полю event и игнорируйте типы, которые вы пока не обрабатываете, — регистрировать что-либо заново для начала их приема не требуется.
  • Хеши проверяются в сети перед отправкой (см. примечание в начале): вебхук либо содержит хеши, каждый из которых включен в блок, либо не отправляется вообще.
  • URL валидируются на предмет SSRF-безопасности при регистрации и при каждом изменении; сервис доставки выполняет повторную валидацию в момент отправки.