文档目录

快速上手

只需五个请求,即可完成从获取报价到链上能量委托的全流程:估算、签名、报价、下单、确认。其中第一步完全公开,无需任何密钥,在注册账户之前即可直接调用。

开始之前

阶段价格估算锁定报价下单购买
请求接口GET /v1/estimatePOST /v1/quotesPOST /v1/orders
API 密钥不需要需要需要
价格约束无约束 —— 随日间时段浮动有约束,锁定 120 秒 (expires_at)直接从账户余额扣款
生成对象无报价单 (qt_…)订单 (ord_…)

各步骤所需准备工作:

步骤您需要准备
1终端命令行(自带 curl)、Node.js 18+ 或 Python 3。无需其他任何依赖。
2–5注册账户并创建具备相应权限的 API 密钥。注册后即可立即创建密钥;只有步骤 4 下单时才需要账户余额。AI Agent 快速接入 详细介绍了两者的创建流程;密钥权限由您自主勾选 —— 系统没有任何预设权限。

建议先在 Nile 测试网演练:https://api-nile.tenergy.me/v1,测试网拥有独立的账户体系、密钥(ak_test_…)以及账本。测试网与主网存在细微差异 —— 详见 网络与环境。

迈出第一单的五个步骤

步骤 1 — 价格估算

GET /v1/estimate 是公开接口:实时查询当前 1 小时 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链上激活费用(当 receiver 未激活时收取;未传 receiver 时为 0)。
total_amount_sun当前下单所需支付的总费用。1 TRX = 1,000,000 SUN。
receiver_activated目标地址是否已激活:true / false(未传地址时为 null)。
as_of估价计算时间。该报价不具备时效锁定效力。

背后的实时价格网格可通过公开接口 GET /v1/prices 查询 —— 建议从接口动态读取开放的档位及其上下限,而不是在代码中硬编码。

步骤 2 — 使用密钥进行请求签名

后续每个私有接口请求都需要携带三个头:X-API-KEY、X-API-TIMESTAMP 以及 X-API-SIGN = base64(HMAC-SHA256(secret, timestamp + METHOD + path + query + body))。下面的通用辅助函数实现了完整规范;身份鉴权 详细剖析了每个部分的原理。

export TENERGY_KEY=ak_test_…      # 控制台中的密钥 ID
export TENERGY_SECRET=sk_test_…   # 创建密钥时展示的 Secret
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!; // Nile 测试网为 ak_test_…
const SECRET = process.env.TENERGY_SECRET!; // 创建密钥时展示的 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 测试签名函数。Secret 错误将返回 401 及 1002 invalid_signature;密钥不存在或已被注销将返回 1006 key_revoked。

步骤 3 — 获取并锁定报价 (Quote)

此步骤为可选操作。报价单会在 120 秒内锁定 total_amount_sun 总价;如果您只需要限制最高价格上限,也可以跳过此步直接在订单中传入 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。重复发送完全相同的请求将直接返回已存在的订单(HTTP 200)且绝不重复扣款,因此网络超时后重新发起请求绝不会导致二次扣费。

tenergy POST /orders '{"quote_id":"qt_01J9Z5NB2K4R","client_order_id":"payout-8821"}'   # 步骤 3 获取的 ID
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"})

返回 HTTP 201 代表订单已创建且扣款成功,并不代表链上委托已完成。通常状态 status 会立即变为 active;如果显示为 allocating 则说明系统正在调度池子进行委托分配。

步骤 5 — 等待订单确认

您可以使用自己的业务单号轮询订单,也可以配置 Webhook 自动接收 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,未交付差额已自动退回 —— 这是一个布尔字段,而非独立的状态枚举。

下一步

  • 身份鉴权— 深入理解规范字符串构造、时钟偏移与权限范围配置。
  • Webhooks 回调— 使用 order.confirmed 事件通知替代轮询,以及签名校验方法。
  • 错误码与频控— 本流程可能遇到的所有错误码及重试规范。
  • 订单接口参考— POST /v1/orders 的完整参数与字段说明。

    ↑ ↓ 切换 · Enter 打开 · Esc 关闭