Быстрый старт
Всего пять запросов отделяют вас от получения котировки до делегирования энергии в блокчейне: оценка, подпись, котировка, заказ, подтверждение. Первый запрос полностью публичен и не требует ключа, поэтому вы можете выполнить его еще до регистрации аккаунта.
Перед началом работы
| Этап | Расчет (Estimate) | Котировка (Quote) | Заказ (Order) |
|---|---|---|---|
| Метод API | GET /v1/estimate | POST /v1/quotes | POST /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, а разница уже возвращена на баланс — это отдельное булево поле, а не отдельный статус.