POST /apiv2/reports/webhooks
Зарегистрируйте URL, и NETTS вызовет его, когда отчет будет готов, избавляя вас от необходимости опрашивать статус вручную.
Эти эндпоинты отделены от вебхуков заказов. Регистрация там не подписывает вас на уведомления об отчетах, и наоборот. Формат передачи данных — подпись, заголовки, логика повторов — абсолютно одинаков, поэтому обработчик, написанный для одного из них, подойдет и для другого.
Базовый URL эндпоинта
https://netts.io/apiv2/reports/webhooksЗаголовки запроса
| Заголовок | Обязательный | Описание |
|---|---|---|
X-API-KEY | да | API-ключ из панели управления |
X-Real-IP | да | Адрес из белого списка ключа |
Основной и резервный
До двух эндпоинтов на аккаунт. primary получает все события. backup используется только после того, как исчерпаны все попытки доставки на основной — и он подписывается своим собственным секретным ключом, а не ключом основного.
Регистрация
curl -s -X POST 'https://netts.io/apiv2/reports/webhooks' \
-H 'X-API-KEY: your-api-key' \
-H 'X-Real-IP: 203.0.113.10' \
-H 'Content-Type: application/json' \
-d '{"url": "https://example.com/netts/reports", "role": "primary"}'{
"status": "success",
"code": 10000,
"data": {
"id": 1,
"url": "https://example.com/netts/reports",
"role": "primary",
"is_active": true,
"created_at": "2026-09-06 17:05:12+00:00",
"updated_at": "2026-09-06 17:05:12+00:00",
"secret": "whsec_<64 hex characters>"
}
}Секретный ключ отображается только один раз, здесь. Он больше никогда не возвращается — ни в списке, ни в эндпоинте чтения. Сохраните его при получении. Если он утерян, сгенерируйте новый:
curl -s -X POST 'https://netts.io/apiv2/reports/webhooks/1/rotate-secret' \
-H 'X-API-KEY: your-api-key' -H 'X-Real-IP: 203.0.113.10'Ротация вступает в силу немедленно, и старый секрет перестает проходить проверку, поэтому сначала разверните новое значение, если вы не можете допустить перерыва в обработке.
Управление
| Метод | Путь | Действие |
|---|---|---|
GET | /apiv2/reports/webhooks | список ваших эндпоинтов, без секретных ключей |
GET | /apiv2/reports/webhooks/{id} | получить один эндпоинт |
PATCH | /apiv2/reports/webhooks/{id} | изменить url или приостановить с помощью is_active: false |
DELETE | /apiv2/reports/webhooks/{id} | удалить эндпоинт |
URL должен быть публичным адресом HTTPS. Loopback-, приватные и link-local адреса отклоняются, как и учетные данные внутри URL. Любой отклоненный запрос возвращает статус 422 с указанием причины. Проверка запускается снова непосредственно перед каждой доставкой, поэтому эндпоинт, который позже начнет разрешаться в приватный адрес, перестанет получать уведомления.
Что мы отправляем
{
"event": "report.ready",
"delivery_id": 4,
"order_id": "REPxxxxxxxxxxxx",
"order_type": "statement",
"client_request_id": "stmt-2026-09-usdt",
"status": "done",
"format": "csv",
"download_url": "/apiv2/reports/REPxxxxxxxxxxxx/download",
"expires_at": "2026-10-06 15:48:04+00:00",
"artifact": { "sha256": "…", "size_bytes": 696 },
"confirmed_at": "2026-09-06T15:48:04Z"
}| Поле | Описание |
|---|---|
event | report.ready — ключ маршрутизации для вашего обработчика |
delivery_id | Ключ дедупликации. Также отправляется в заголовке X-Netts-Delivery |
order_id | Номер заказа, полученный вами при постановке отчета в очередь |
order_type | statement или balance_at_date |
download_url | Путь для скачивания файла относительно https://netts.io |
artifact.sha256 | Контрольная сумма для проверки скачанного файла |
confirmed_at | UTC |
Все временные метки указаны в формате UTC.
Проверка подписи
X-Netts-Signature: sha256=<hex>
X-Netts-Timestamp: <unix seconds>
X-Netts-Delivery: <delivery_id>Подпись представляет собой HMAC-SHA256 от "<timestamp>." + raw body, вычисленный с использованием секретного ключа того эндпоинта, который получил запрос. Сравнивайте за константное время и отклоняйте любые запросы, временная метка которых выходит за пределы окна в ±5 минут.
import hmac, hashlib, time
def verify(raw_body: bytes, sig_header: str, ts_header: str, secret: str) -> bool:
if abs(time.time() - int(ts_header)) > 300: # anti-replay
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)Выполняйте подписание с помощью секрета того URL, на который поступил запрос: у основного и резервного эндпоинтов секреты различаются.
Доставка гарантируется по модели at-least-once
Потеря ответа приводит к повторной отправке, поэтому одно и то же событие может прийти дважды.
- Дедуплицируйте по
delivery_id. Повторный запрос должен быть безопасной холостой операцией (no-op) на вашей стороне. - Проверяйте подпись до выполнения действий, а не после.
- Отвечайте кодом
2xxтолько после сохранения события. Любой другой ответ или тайм-аут считаются ошибкой и приводят к повторным попыткам.
Повторные попытки на один эндпоинт отправляются через 1 минуту, 5 минут, 15 минут, 1 час, 6 часов и 24 часа — всего шесть попыток, охватывающих чуть более 31 часа. Когда они исчерпаны, и вы зарегистрировали backup, доставка переключается на него, и расписание начинается заново с собственным секретом резервного эндпоинта. Значение delivery_id остается неизменным на всем протяжении, поэтому событие, завершившееся ошибкой на основном эндпоинте и успешно доставленное на резервный, остается одним и тем же событием.
Перенаправления (редиректы) не поддерживаются.
Ограничения частоты запросов
10 запросов в секунду на эндпоинт, суммарно для всех клиентов.
Ошибки
Регистрация возвращает 201, удаление — 204 без тела ответа, все остальные запросы — 200.
| HTTP | Значение |
|---|---|
401 | ключ отсутствует или недействителен, либо IP-адрес источника не добавлен в белый список |
404 | такой эндпоинт отсутствует в вашем аккаунте |
409 | запрошенная роль уже занята — role primary is already taken |
422 | URL был отклонен, либо тело PATCH не содержит изменений |
429 | превышено ограничение частоты запросов |
Отклоненный URL возвращается со статусом 422 и явным описанием причины, чтобы вы могли показать ее тому, кто его ввел:
{"status": "error", "code": -4, "msg": "url: Value error, invalid webhook_url: resolved address 127.0.0.1 is not public"}Формулировки ошибок: only https:// URLs are allowed, credentials in URL are not allowed и resolved address <ip> is not public. Последняя проверяется во время регистрации и повторно непосредственно перед каждой доставкой, поэтому имя хоста, которое позже станет указывать на приватный адрес, перестанет получать уведомления.
Связанные разделы
- Файлы выписок — заказ отчета, который запускает это уведомление
- Вебхуки заказов — отдельный реестр для событий Energy, Bandwidth и активации