POST /apiv2/withdraw
Вывод TRX с вашего баланса Netts на адрес TRON. Запрос немедленно возвращает номер заказа; фактическая выплата в сети выполняется асинхронно бэкендом (в течение ~5 минут). Отслеживайте результат с помощью опроса эндпоинта статуса или настроив вебхук.
ℹ️ Как это работает. Создание заявки на вывод сразу резервирует сумму с вашего баланса (баланс списывается в момент принятия заказа). Затем фоновый демон отправляет TRX и помечает заказ как
completedилиfailed. В начальном ответе нет синхронного результата транзакции в сети — вы всегда сначала получаете подтверждение со статусомpending.Перед принятием заказа проверяется адрес назначения. Если вывод на этот адрес недоступен или проверку невозможно выполнить в данный момент, запрос отклоняется: заказ не создается, и с вашего баланса ничего не резервируется.
URL эндпоинта
POST https://netts.io/apiv2/withdrawЗаголовки запроса
| Заголовок | Обязательный | Описание |
|---|---|---|
| Content-Type | Да | application/json |
| X-API-KEY | Да | Ваш API-ключ из панели управления Netts |
| X-Idempotency-Key | Нет | Опциональный ключ, сгенерированный клиентом (base64), для безопасных повторных попыток без двойного вывода. Если опущен, сервер формирует его автоматически. Это значение становится вашим orderId. |
Тело запроса
{
"amount": 15,
"address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}Параметры запроса
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| amount | число | Да | Сумма брутто в TRX (минимум 3). Комиссия вычитается из этой суммы — получатель получает amount − fee (net). |
| address | строка | Да | Адрес назначения TRON (T…, 34 символа, base58). Проверяется перед принятием заказа. |
| sub_and_robot_out | логический | Нет | Режим выплат робота/субаккаунта: применяется комиссия 2 TRX вместо 1 TRX. По умолчанию false. |
Комиссия. Фиксированная комиссия удерживается из суммы брутто
amount: стандартно 1 TRX или 2 TRX, еслиsub_and_robot_out = true. Заказ отклоняется, еслиamount − fee ≤ 0.
Примеры
В приведенных ниже примерах также формируется и отправляется
X-Idempotency-Key, чтобы случайный повтор не создал второй вывод. Полные правила см. в разделе Идемпотентность.
cURL
API_KEY="your_api_key"
ADDR="TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
AMOUNT=15
NONCE=$(( $(date +%s) / 2 )) # stable for retries within a 2s window; or your own order UUID
# X-Idempotency-Key = base64url( HMAC-SHA256( API_KEY, "addr:amount:nonce" ) )
IDEMP=$(printf '%s' "${ADDR}:${AMOUNT}:${NONCE}" \
| openssl dgst -sha256 -hmac "$API_KEY" -binary | basenc --base64url | tr -d '=')
curl -X POST https://netts.io/apiv2/withdraw \
-H "Content-Type: application/json" \
-H "X-API-KEY: $API_KEY" \
-H "X-Idempotency-Key: $IDEMP" \
-d "{\"amount\": $AMOUNT, \"address\": \"$ADDR\"}"Python
import time, hmac, hashlib, base64, requests
API_KEY = "your_api_key"
url = "https://netts.io/apiv2/withdraw"
payload = {"amount": 15, "address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
# X-Idempotency-Key = base64url( HMAC-SHA256( API_KEY, "addr:amount:nonce" ) ), padding stripped.
# Generate ONCE per order and resend the same value on every retry.
nonce = str(int(time.time() // 2)) # 2s bucket; or your own order UUID
message = f"{payload['address']}:{payload['amount']}:{nonce}"
idem_key = base64.urlsafe_b64encode(
hmac.new(API_KEY.encode(), message.encode(), hashlib.sha256).digest()
).decode().rstrip("=")
headers = {
"Content-Type": "application/json",
"X-API-KEY": API_KEY,
"X-Idempotency-Key": idem_key,
}
resp = requests.post(url, headers=headers, json=payload)
detail = resp.json().get("detail", {})
if resp.status_code == 202 and detail.get("status") == "pending":
d = detail["data"]
print(f"Order ID: {d['orderId']}") # use it for the status endpoint / webhook
print(f"Net to recipient: {d['net']} TRX (fee {d['fee']})")
else:
print(f"Code {detail.get('code')}: {detail.get('msg', detail)}")Ответ
Принято — вывод поставлен в очередь (202 Accepted)
Сумма зарезервирована с вашего баланса, выплата запланирована. Опрашивайте эндпоинт статуса (или ожидайте вебхук), пока статус не изменится на completed / failed.
{
"detail": {
"code": 10000,
"status": "pending",
"msg": "Withdrawal request accepted, processing within 5 minutes.",
"data": {
"orderId": "EXAMPLEorderId0000000000000000000000000000Aa",
"amount": 15.0,
"fee": 1.0,
"net": 14.0,
"address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
| detail.code | целое число | 10000 принято |
| detail.status | строка | pending |
| detail.data.orderId | строка | Номер заказа — URL-безопасная строка из 43 символов. Используйте ее для эндпоинта статуса; также она идентифицирует заказ в данных вебхука. |
| detail.data.amount | число | Запрошенная сумма брутто (TRX) |
| detail.data.fee | число | Удержанная комиссия (1 или 2 TRX) |
| detail.data.net | число | Сумма, получаемая адресатом (amount − fee) |
| detail.data.address | строка | Адрес назначения |
Эндпоинт статуса
GET https://netts.io/apiv2/withdraw/status/{orderId}Заголовки: X-API-KEY (заказ должен принадлежать аутентифицированному пользователю). orderId является URL-безопасным — передавайте его как есть, дополнительное URL-кодирование не требуется.
| Состояние заказа | HTTP | code | status |
|---|---|---|---|
| Завершен (TRX отправлены) | 200 | 10000 | completed (с processed_at) |
| В очереди / отправляется | 200 | 10001 | pending |
| Ошибка | 200 | 5003 | failed (с error_message) |
| Не найден / чужой заказ | 404 | -1 | — |
{
"detail": {
"code": 10000,
"status": "completed",
"data": {
"orderId": "EXAMPLEorderId0000000000000000000000000000Aa",
"amount": 15.0, "fee": 1.0, "net": 14.0,
"address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"processed_at": "2026-01-01 00:00:00+00:00"
}
}
}Аккаунты субаккаунтов
Выводы для субаккаунтов работают точно так же, как и для обычных пользователей — просто с собственным API-ключом субаккаунта. Субаккаунт вызывает этот же эндпоинт POST /apiv2/withdraw, аутентифицируясь собственным ключом; вывод списывается с собственного баланса этого субаккаунта и отправляется на address, указанный в запросе. Тот же минимум, та же комиссия (1 TRX), та же проверка адреса, тот же процесс. Отдельного эндпоинта для субаккаунтов нет — каждый аккаунт, основной или субаккаунт, выводит только свой собственный баланс со своим ключом.
Вебхуки
Вместо постоянного опроса настройте вебхук один раз, и Netts будет отправлять подписанное POST-уведомление, когда каждый из ваших выводов достигнет финального состояния (completed / failed). Вебхук сохраняется для каждого пользователя индивидуально и применяется к выводам этого аккаунта. Если вебхук не настроен, просто опрашивайте эндпоинт статуса.
Настройка / просмотр / удаление
POST https://netts.io/apiv2/withdraw/webhook # create or update
GET https://netts.io/apiv2/withdraw/webhook # view current config (secret is never returned)
DELETE https://netts.io/apiv2/withdraw/webhook # unsubscribeЗаголовки: X-API-KEY.
// POST body
{
"callback_url": "https://your-server.example/netts/withdraw-hook",
"secret": "your_shared_secret_min_8_chars",
"enabled": true
}| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| callback_url | строка | Да | http(s) URL (≤ 2048 символов), принимающий POST-запрос |
| secret | строка | Да | Общий секретный ключ (8…256 символов), используемый для подписи каждого тела запроса |
| enabled | логический | Нет | Включение/отключение доставки без удаления конфигурации. По умолчанию true |
GET возвращает { callback_url, enabled, secret_set, updated_at } — сам секретный ключ никогда не возвращается в ответе.
Данные доставки
Netts отправляет POST на ваш callback_url с заголовком X-Netts-Signature: base64( HMAC-SHA256( secret, raw_body ) ) и следующим телом JSON:
{
"orderId": "EXAMPLEorderId0000000000000000000000000000Aa",
"status": "completed",
"amount": 15.0,
"fee": 1.0,
"net": 14.0,
"address": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"processed_at": "2026-01-01 00:00:00+00:00",
"error_message": null
}statusимеет значениеcompletedилиfailed(приfailedзаполняетсяerror_message).
Проверка подписи
Подпись вычисляется по каноническому JSON тела запроса: ключи отсортированы, пробелы отсутствуют (separators=(",", ":")). Рассчитайте ее тем же способом и сравните.
import hmac, hashlib, base64, json
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = base64.b64encode(
hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
).decode()
return hmac.compare_digest(expected, signature_header)
# Flask example: verify against the EXACT bytes received, then parse.
# if verify(request.get_data(), request.headers["X-Netts-Signature"], SECRET): ...Всегда выполняйте проверку по исходным полученным байтам. Если вы повторно сериализуете разобранный JSON, воспроизведите каноническую форму:
json.dumps(payload, ensure_ascii=False, separators=(",",":"), sort_keys=True).
Гарантии доставки
- Отвечайте кодом HTTP 2xx для подтверждения. Любой другой ответ (или таймаут) считается неудачной попыткой.
- До 3 попыток на заказ в пределах 21-минутного окна с момента создания заказа (интервал повторов ≈ 5 минут). После этого попытки прекращаются — используйте эндпоинт статуса.
- Доставка дедуплицируется: каждый заказ успешно доставляется не более одного раза.
- Сделайте ваш обработчик идемпотентным по
orderId.
Ответы с ошибкой
Ошибка аутентификации (401)
{ "detail": { "code": -1, "msg": "Invalid API key or IP not in whitelist" } }Недостаточно средств на балансе (403)
{ "detail": { "code": 1004, "status": "failed", "msg": "Insufficient balance: 2.0 < 15 TRX" } }Адрес недоступен (403)
Вывод на этот адрес назначения не принимается. Заказ не создается, и средства не резервируются. Используйте другой адрес или обратитесь в службу поддержки.
{ "detail": { "code": 1006, "status": "failed", "msg": "Withdrawals to this address are not available. Contact support." } }Проверка адреса недоступна (503)
Не удалось проверить адрес назначения в данный момент. Заказ не создается, и средства не резервируются. Повторите попытку позже — можно использовать тот же X-Idempotency-Key.
{ "detail": { "code": 1009, "status": "failed", "msg": "Address check is temporarily unavailable. No request was created. Retry shortly." } }Уже есть активный вывод в обработке (409)
С вашего баланса может выполняться только один незавершенный вывод одновременно. Дождитесь обработки текущего вывода.
{ "detail": { "code": 4090, "status": "failed", "msg": "You have a pending withdrawal. Wait until it is processed." } }Ошибка валидации (400)
{ "detail": { "code": 5004, "status": "failed", "msg": "Minimum withdrawal is 3 TRX" } }Справочник кодов ошибок
| Код | Описание | HTTP-статус |
|---|---|---|
10000 | Принято (вывод в очереди) / Завершено (эндпоинт статуса) | 202 / 200 |
10001 | В обработке — в очереди или отправляется (эндпоинт статуса) | 200 |
208 | Дубликат ранее принятого запроса — кешированный ответ | 208 |
- | Тот же запрос еще обрабатывается (пока не повторяйте) | 409 |
4090 | У вас уже есть вывод в обработке | 409 |
-1 | Недействительный API-ключ / IP не в белом списке, или заказ не найден | 401 / 404 |
1004 | Недостаточно средств на балансе | 403 |
1006 | Вывод на этот адрес недоступен | 403 |
1009 | Проверка адреса временно недоступна — заказ не создан, повторите попытку позже | 503 |
5004 | Ошибка валидации (сумма < 3, комиссия ≥ суммы, некорректный адрес, некорректный ключ идемпотентности) | 400 |
5003 | Ошибка вывода / сервис недоступен | 200 (статус) / 503 |
5000 | Внутренняя ошибка сервера | 500 |
Ограничения частоты запросов
Ограничение действует на каждый API-ключ (заголовок X-API-KEY):
| Период | Лимит |
|---|---|
| 1 секунда | 5 запросов |
| 1 минута | 150 запросов |
Превышен лимит запросов (429)
{ "message": "API rate limit exceeded" }Идемпотентность
Передавайте опциональный заголовок X-Idempotency-Key, чтобы случайный повтор не привел к повторному выводу — будет возвращен исходный ответ с HTTP-кодом 208. Если заголовок не передан, сервер генерирует ключ автоматически на основе параметров запроса в пределах короткого интервала времени. Этот ключ также является вашим orderId.
Как сформировать ключ
Ключ представляет собой base64url( HMAC-SHA256( secret, message ) ) с удаленным заполнением = — URL-безопасную строку из 43 символов, где:
- secret = ваш API-ключ (
X-API-KEY); - message = поля, объединенные через
:—address:amount:nonce.
nonce — это любое значение, которое является неизменным при повторных попытках одного и того же логического заказа, но различается между отдельными заказами, например, UUID, который вы сохраняете для этого заказа, или округленный временной интервал. Генерируйте ключ один раз для каждого заказа и отправляйте точно такое же значение при каждой повторной попытке.
import hmac, hashlib, base64, time
def make_idempotency_key(api_key, address, amount, nonce=None):
if nonce is None:
nonce = str(int(time.time() // 2)) # 2-second bucket; or your own order UUID
message = f"{address}:{amount}:{nonce}"
digest = hmac.new(api_key.encode(), message.encode(), hashlib.sha256).digest()
return base64.urlsafe_b64encode(digest).decode().rstrip("=") # 43-char URL-safeВалидация. Переданный
X-Idempotency-Keyдолжен содержать от 16 до 64 символов из набораA–Z a–z 0–9 + / = _ -. Некорректный или слишком длинный ключ отклоняется с кодом HTTP 400 (code 5004).
| Код состояния | Значение |
|---|---|
| 202 | Принято (первый запрос) |
| 208 | Уже принято — возвращен кешированный ответ (повторный вывод не создается) |
| 409 | Тот же запрос сейчас находится в обработке — подождите, пока не повторяйте |
Повтор после ошибки. Кешируются только успешно принятые результаты. Если предыдущая попытка завершилась ошибкой (например, недостаточно средств, ошибка валидации), вы можете безопасно повторить запрос с тем же ключом — попытка будет выполнена заново, а не вернет старую ошибку. Пока попытка находится в процессе выполнения, вы получите
409; подождите и повторите запрос.
Примечания
- Асинхронная выплата. Ответ всегда представляет собой подтверждение со статусом
pending; TRX отправляются фоновым демоном, обычно в течение ~5 минут. Для получения результата используйте эндпоинт статуса или вебхук. - Баланс резервируется немедленно в момент принятия заказа (а не тогда, когда TRX будут фактически отправлены).
- Минимум: 3 TRX. Комиссия: 1 TRX (или 2 TRX при
sub_and_robot_out), удерживается из суммы бруттоamount; получатель получаетnet = amount − fee. - Только один активный вывод одновременно с вашего собственного баланса (
code 4090). - Проверка адреса назначения. Каждый запрос проверяется до принятия заказа:
code 1006(HTTP 403) — вывод на этот адрес недоступен;code 1009(HTTP 503) — проверка временно недоступна, повторите попытку позже. В обоих случаях заказ не создается. - Субаккаунты выполняют вывод точно так же, как и обычные пользователи — тот же эндпоинт
POST /apiv2/withdraw, те же правила, но с аутентификацией собственным API-ключом субаккаунта. Субаккаунт выводит свой собственный баланс на указанный имaddress. Отдельного эндпоинта для субаккаунтов нет. - orderId представляет собой URL-безопасную строку из 43 символов; передавайте ее в URL статуса как есть (кодирование не требуется).
- Вебхуки: настраиваются для каждого пользователя индивидуально, подписываются заголовком
X-Netts-Signature; до 3 попыток в пределах 21-минутного окна. Настройка черезPOST /apiv2/withdraw/webhook.