Меню документации

Быстрый старт

Всего пять запросов отделяют вас от получения котировки до делегирования энергии в блокчейне: оценка, подпись, котировка, заказ, подтверждение. Первый запрос полностью публичен и не требует ключа, поэтому вы можете выполнить его еще до регистрации аккаунта.

Перед началом работы

ЭтапРасчет (Estimate)Котировка (Quote)Заказ (Order)
Метод APIGET /v1/estimatePOST /v1/quotesPOST /v1/orders
API-ключНе требуетсяОбязателенОбязателен
ОбязательстваНет — цена может измениться при смене периодаДа, цена зафиксирована на 120 с (expires_at)Списывает средства с баланса
Создаваемый объектНичегоКотировка (qt_…)Заказ (ord_…)

Что требуется для выполнения шагов:

ШагиЧто вам потребуется
1Терминал с утилитой curl, Node.js 18+ или Python 3. Больше ничего.
2–5Аккаунт и API-ключ с необходимыми областями видимости (scopes). Ключи доступны сразу после регистрации; баланс требуется только на 4 шаге при оформлении заказа. В быстром старте для агента описано создание аккаунта и ключа; разрешения ключа выбираются вами вручную — дефолтного набора нет.

Рекомендуется сначала протестировать интеграцию в тестнете Nile: https://api-nile.tenergy.me/v1, где действуют отдельный аккаунт, тестовые ключи (ak_test_…) и свой баланс. Nile имеет небольшие отличия от основной сети — подробности в разделе Окружения.

Пять шагов до вашего первого заказа

Шаг 1 — Рассчитайте стоимость

Эндпоинт GET /v1/estimate публичен: он показывает текущую стоимость 65 000 энергии на один час с учетом комиссии за активацию, если адрес получателя еще не использовался в сети TRON.

curl -sG https://api-nile.tenergy.me/v1/estimate \
  -d resource=energy -d amount=65000 -d tier=1h \
  -d receiver=TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE
const url = new URL("https://api-nile.tenergy.me/v1/estimate");
url.search = new URLSearchParams({
  resource: "energy",
  amount: "65000",
  tier: "1h",
  receiver: "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
}).toString();

const estimate = await (await fetch(url)).json();
console.log(estimate.total_amount_sun, "SUN");
import json, urllib.parse, urllib.request

query = urllib.parse.urlencode({
    "resource": "energy",
    "amount": 65000,
    "tier": "1h",
    "receiver": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
})
with urllib.request.urlopen(f"https://api-nile.tenergy.me/v1/estimate?{query}") as res:
    estimate = json.load(res)
print(estimate["total_amount_sun"], "SUN")
Поле ответаОписание
price_sun_per_unitСтоимость единицы энергии в SUN для выбранного тарифа в текущем периоде суток.
energy_amount_sunСтоимость аренды энергии: amount × price_sun_per_unit.
activate_amount_sunСтоимость активации адреса в сети TRON (если receiver не активирован; 0, если адрес не передан).
total_amount_sunИтоговая сумма к списанию за заказ. 1 TRX = 1 000 000 SUN.
receiver_activatedСтатус активации адреса: true / false (null, если адрес не передан).
as_ofВремя расчета стоимости. Оценка носит ориентировочный характер и не фиксирует цену.

Полная сетка действующих тарифов доступна через публичный запрос GET /v1/prices — запрашивайте доступные тиры и лимиты из API, а не хардкодьте их.

Шаг 2 — Подписание запросов с помощью ключа

Каждый приватный вызов должен содержать три заголовка: X-API-KEY, X-API-TIMESTAMP и X-API-SIGN = base64(HMAC-SHA256(secret, timestamp + METHOD + path + query + body)). Функция-хелпер ниже полностью реализует эту логику; в разделе Аутентификация разобрана каждая ее деталь.

export TENERGY_KEY=ak_test_…      # идентификатор ключа из кабинета
export TENERGY_SECRET=sk_test_…   # секрет, показанный один раз при создании ключа
BASE=https://api-nile.tenergy.me/v1

# tenergy METHOD PATH [BODY] — PATH указывается относительно /v1 и может содержать query-параметры
tenergy() {
  local method=$1 path=$2 body=${3:-}
  local ts sign
  ts=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
  sign=$(printf '%s' "$ts$method/v1$path$body" \
    | openssl dgst -sha256 -hmac "$TENERGY_SECRET" -binary | base64)
  curl -sS -X "$method" "$BASE$path" \
    -H "X-API-KEY: $TENERGY_KEY" -H "X-API-TIMESTAMP: $ts" -H "X-API-SIGN: $sign" \
    ${body:+-H "Content-Type: application/json" --data-raw "$body"}
}
import { createHmac } from "node:crypto";

const BASE = "https://api-nile.tenergy.me/v1";
const KEY = process.env.TENERGY_KEY!; // ak_test_… в Nile
const SECRET = process.env.TENERGY_SECRET!; // секрет, сохраненный при создании ключа

export async function tenergy(method: string, pathAndQuery: string, body?: unknown) {
  const raw = body === undefined ? "" : JSON.stringify(body); // подписывайте ровно те байты, которые отправляете
  const url = new URL(BASE + pathAndQuery);
  const ts = new Date().toISOString();
  const sign = createHmac("sha256", SECRET)
    .update(ts + method + url.pathname + url.search + raw)
    .digest("base64");
  const res = await fetch(url, {
    method,
    headers: {
      "X-API-KEY": KEY,
      "X-API-TIMESTAMP": ts,
      "X-API-SIGN": sign,
      ...(raw ? { "Content-Type": "application/json" } : {}),
    },
    body: raw || undefined,
  });
  return res.json();
}
import base64, hashlib, hmac, json, os, urllib.error, urllib.request
from datetime import datetime, timezone
from urllib.parse import urlsplit

BASE = "https://api-nile.tenergy.me/v1"
KEY, SECRET = os.environ["TENERGY_KEY"], os.environ["TENERGY_SECRET"]

def tenergy(method, path_and_query, body=None):
    raw = "" if body is None else json.dumps(body, separators=(",", ":"))  # подписывайте ровно те байты, которые отправляете
    url = BASE + path_and_query
    parts = urlsplit(url)
    ts = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    signed = ts + method + parts.path + (f"?{parts.query}" if parts.query else "") + raw
    sign = base64.b64encode(hmac.new(SECRET.encode(), signed.encode(), hashlib.sha256).digest()).decode()
    headers = {"X-API-KEY": KEY, "X-API-TIMESTAMP": ts, "X-API-SIGN": sign}
    if raw:
        headers["Content-Type"] = "application/json"
    req = urllib.request.Request(url, data=raw.encode() or None, method=method, headers=headers)
    try:
        with urllib.request.urlopen(req) as res:
            return json.load(res)
    except urllib.error.HTTPError as err:
        return json.load(err)  # объект ошибки: анализируйте error.slug

Проверьте работу функции вызовом GET /v1/balance. Неверный секрет вернет 401 и ошибку 1002 invalid_signature; несуществующий или отозванный ключ вернет 1006 key_revoked.

Шаг 3 — Зафиксируйте цену через котировку (Quote)

Опциональный шаг. Котировка фиксирует общую сумму total_amount_sun на 120 секунд. Если вам достаточно ограничить максимальную цену, этот шаг можно пропустить и передать параметр max_price_sun напрямую в заказе.

tenergy POST /quotes '{"resource":"energy","amount":65000,"tier":"1h","receiver":"TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE"}'
const quote = await tenergy("POST", "/quotes", {
  resource: "energy",
  amount: 65000,
  tier: "1h",
  receiver: "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
});
quote = tenergy("POST", "/quotes", {
    "resource": "energy",
    "amount": 65000,
    "tier": "1h",
    "receiver": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
})

Ответ содержит идентификатор id, сумму total_amount_sun и время истечения expires_at. Истекшая котировка отклоняется с ошибкой 3005 quote_expired — в этом случае запросите новую котировку.

Шаг 4 — Оформите заказ

Всегда передавайте собственный идентификатор client_order_id. Повторный запрос с идентичным id вернет созданный ранее заказ с кодом 200 и не спишет деньги повторно — благодаря этому при тайм-ауте сети можно безопасно повторять запрос.

tenergy POST /orders '{"quote_id":"qt_01J9Z5NB2K4R","client_order_id":"payout-8821"}'   # id из шага 3
const order = await tenergy("POST", "/orders", {
  quote_id: quote.id,
  client_order_id: "payout-8821",
});
order = tenergy("POST", "/orders", {"quote_id": quote["id"], "client_order_id": "payout-8821"})

Код ответа 201 означает, что заказ успешно принят и оплачен, но делегирование еще может выполняться. Обычно статус status сразу переходит в active; статус allocating означает, что подбор пулов для делегирования еще продолжается.

Шаг 5 — Дождитесь подтверждения заказа

Вы можете опрашивать состояние заказа по своему client_order_id либо настроить вебхук и получить событие order.confirmed.

tenergy GET /orders/cid:payout-8821
const current = await tenergy("GET", "/orders/cid:payout-8821");
if (current.partial) console.log("делегировано", current.delivered_amount, "из", current.amount);
current = tenergy("GET", "/orders/cid:payout-8821")
if current.get("partial"):
    print("делегировано", current["delivered_amount"], "из", current["amount"])
Статус заказа statusЧто он означает
allocating, delegatedТранзакция делегирования в пути — повторите опрос через 1 секунду.
confirmed, activeЭнергия поступила на адрес получателя. Оба статуса считаются успешным завершением.
failedДелегирование не состоялось; списанные средства полностью возвращены на баланс.
expired, reclaimedСрок аренды энергии завершился.

Обращайте внимание на флаг partial. Частично выполненный заказ также имеет статус confirmed/active, но объем delivered_amount в нем меньше заказанного amount, а разница уже возвращена на баланс — это отдельное булево поле, а не отдельный статус.

Следующие шаги

    ↑ ↓ — выбрать · Enter — открыть · Esc — закрыть