跳到主要内容

Webhook 事件

Webhook 是可选的推送通道:无需轮询,TRONAgg 会在事件发生时向你的 URL POST 一条带签名的 JSON 事件。轮询(GET /order/{id}、GET /deposits)始终可用,不依赖这里的任何内容——只有想要推送时才需要配置 Webhook。

可在 工作台 → 通知 中创建,或通过 POST /webhooks 创建。

投递​

每次投递是一个 HTTPS POST,携带一个事件:

POST https://your-service.example.com/tronagg/webhook
{
"id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"type": "order.status_changed",
"created_at": "2026-07-12T10:30:00.123456+00:00",
"audience": "customer",
"data": { }
}
字段含义
id事件唯一 id——按它去重。
type事件类型,同时出现在 X-TronAgg-Event 请求头中。
created_at事件发生时间:带 UTC 偏移和微秒的 ISO-8601,例如 2026-07-12T10:30:00.123456+00:00。
audience关于你自己账户的事件为 customer。分销商账户还会收到 reseller 事件,内容涉及其客户的订单及其分销商余额——见分销商事件。notification.test 中没有此字段。
data事件专属负载——见事件目录。
请求头值
Content-Typeapplication/json
X-TronAgg-Event事件 type。
X-TronAgg-Delivery投递 id,重试之间保持不变。
X-TronAgg-Timestamp本次尝试签名时的 Unix 秒数。
X-TronAgg-Signaturev1=<hex>——验证它。
快速响应,预期重复

请在 10 秒内返回 2xx,把耗时的处理放到请求路径之外。重定向不会被跟随:3xx 与其他任何非 2xx 响应、超时或连接错误一样,计为失败。

失败的投递最多再重试 4 次——分别在每次失败尝试之后约 1 分钟、5 分钟、30 分钟和 2 小时——之后被丢弃。同一事件可能到达不止一次,因此请按 id 去重。事件不保证按顺序到达。如果你不能错过任何状态,请用 GET /order/{id} 或 GET /orders 对账。

验证签名​

签名是以你的 Webhook 签名密钥(只在创建时显示一次)为密钥,对 "{timestamp}.{raw_body}" 计算的 HMAC-SHA256,经 hex 编码后以 v1=<hex> 形式发送。{timestamp} 就是收到的 X-TronAgg-Timestamp 请求头原值。

  • 对原始请求字节进行验证,先于任何 JSON 解析。
  • 使用恒定时间比较。
  • 拒绝 X-TronAgg-Timestamp 与你的时钟相差超过 5 分钟的投递。每次重试都会用新的时间戳重新签名,因此真实的投递总能通过这项检查。
verify.py
import hashlib
import hmac
import time

TOLERANCE_SECONDS = 300


def verify(secret: str, timestamp: str | None, body: bytes, signature_header: str | None) -> bool:
"""timestamp = X-TronAgg-Timestamp,signature_header = X-TronAgg-Signature,body = 原始字节。"""
if not timestamp or not signature_header:
return False
try:
signed_at = int(timestamp)
except ValueError:
return False
if abs(time.time() - signed_at) > TOLERANCE_SECONDS:
return False
expected = "v1=" + hmac.new(
secret.encode(), timestamp.encode() + b"." + body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected.encode(), signature_header.encode())
同样的检查(JavaScript / Node)
verify.js
const crypto = require("node:crypto");

const TOLERANCE_SECONDS = 300;

// timestamp = X-TronAgg-Timestamp,signatureHeader = X-TronAgg-Signature,
// rawBody = 收到的原始字节组成的 Buffer。
function verify(secret, timestamp, rawBody, signatureHeader) {
if (typeof timestamp !== "string" || typeof signatureHeader !== "string") return false;
const signedAt = Number(timestamp);
if (!Number.isInteger(signedAt)) return false;
if (Math.abs(Date.now() / 1000 - signedAt) > TOLERANCE_SECONDS) return false;

const expected = Buffer.from(
"v1=" +
crypto
.createHmac("sha256", secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex")
);
const received = Buffer.from(signatureHeader);
// 长度不同的 Buffer 会让 timingSafeEqual 抛出异常。
if (received.length !== expected.length) return false;
return crypto.timingSafeEqual(expected, received);
}

事件​

On 事件在新建的 Webhook 上开箱即开启;Off 事件初始为关闭——可在 工作台 → 通知 中按 Webhook 单独开关其中任何一个。通过 POST /webhooks 创建的 Webhook 还会开启 order.status_changed;在工作台中创建的 Webhook 初始时该事件为关闭。

默认值在创建 Webhook 时生效。TRONAgg 之后新增的事件,在已有的 Webhook 上初始为关闭——请在 工作台 → 通知 中开启。

请忽略你不认识的事件 type,并仍然返回 2xx:未来可能新增事件。

事件触发时机默认
order.status_changed订单完结——COMPLETED 或 FAILED。✅ On(API)
balance.credited一笔充值,或一笔从分销商余额转入的款项,记入你的余额。✅ On
balance.changed每一笔余额变动——包括扣款。— Off
autoenergy.delegatedAutoEnergy 覆盖了一笔转账。✅ On
autoenergy.closedTRONAgg 自动停止了一个 AutoEnergy 订阅。✅ On
agent.sale一笔 Energy Agent 付款转换为能量订单。✅ On
agent.payment_skipped一笔 Energy Agent 付款被跳过。✅ On
energy_pass.renewed你的 Energy Pass 续订了一个新周期。✅ On
energy_pass.expired你的 Energy Pass 结束。✅ On
vault.payout.sent为你的某个地址发出了一笔能量质押结算。✅ On
autoenergy.activated一个 AutoEnergy 订阅生效。— Off
balance.low余额跌破你设置的阈值。— Off
account.daily_summary每天一次的账户摘要。— Off

provider.demand_changed 仅面向容量合作伙伴。它会出现在你的 Webhook 订阅列表中,但永远不会发送给其他账户。

TRONAgg Radar 地址事件​

TRONAgg Radar 每条提醒都会发送一个 address.* 事件。连接一个 Webhook,并在 TRONAgg Radar → 提醒 中选择 入账与转账、能量添加、问题 和 安全 分组。对于新连接的客户渠道,除非你关闭某个分组,否则所有地址事件均为开启状态;所选渠道按场景生效。即使繁忙的 Telegram 聊天会合并为汇总,Webhook 也从不合并。

每个 data 对象都包含 address_id(UUID 字符串)、Base58Check 格式的 address、display_name、可为空的 label 和 external_id、scenario_id(UUID 字符串)、scenario_name 以及 event_id(整数 Radar 信号 ID,与 REST 时间线事件 ID 相同)。链上事件包含 tx_id、整数 block_number、UTC block_time 和 finality(head、solid、reverted);状态类事件同样带有这些键,但其中的引用可能为空;能量操作类事件只有在由链上事件触发时才包含这些键。到期事件不包含链上引用。场景选定的发送目标永远不会出现在 Webhook 的 data 中。余额未知、费用估算以及完整负载示例中描述的其他可为空字段,都可能为 null。

分组事件额外的 data 键与单位含义
入账与转账address.usdt.receivedasset: "USDT"、direction: "in"、amount、balance:六位小数的 USDT 字符串(balance 可为空);counterparty:Base58Check官方 USDT 转入。
入账与转账address.usdt.sentasset: "USDT"、direction: "out"、amount、balance:六位小数的 USDT 字符串(balance 可为空);counterparty:Base58Check官方 USDT 转出。
入账与转账address.trx.receivedasset: "TRX"、direction: "in"、amount、balance:整数 SUN(balance 可为空);amount_trx:六位小数的 TRX 字符串;counterparty:Base58CheckTRX 转入(至少 10 TRX)。
入账与转账address.trx.sentasset: "TRX"、direction: "out"、amount、balance:整数 SUN(balance 可为空);amount_trx:六位小数的 TRX 字符串;counterparty:Base58CheckTRX 转出(至少 10 TRX)。
能量添加address.sweep.readyepisode_id:UUID 字符串;balance:可为空的六位小数 USDT 字符串;energy_available、energy_required:整数能量;expires_at:可为空的 UTC 时间戳;source:radar 或 existing能量足以按所选预设完成一笔转账;请等收到此事件后再归集。
能量添加address.energy.chargedaction_id、order_id:UUID 字符串(order_id 可为空);energy:整数能量;total_price_sun:整数 SUN;delegation_txid:可为空的字符串;expires_at:可为空的 UTC 时间戳添加操作完成后,能量已交付。
能量添加address.charge.skippedaction_id:UUID 字符串;reason:radar_disabled、actions_disabled、energy_paused、outside_hours、frozen、contract、autoenergy_active、no_bandwidth、budget、limit、insufficient_balance添加被跳过或延后;部分记录在案的跳过是静默的,不会发送 Webhook。
能量添加address.charge.failedaction_id:UUID 字符串;reason:purchase_failed 或 order_failed添加失败。
能量添加address.energy.expiredaction_id:UUID 字符串;order_id:可为空的 UUID 字符串;energy:可为空的整数能量;expires_at:可为空的 UTC 时间戳委托的能量在进行中的归集等待结束之前已到期。
问题address.energy.insufficientenergy_available、energy_required、deficit:整数能量(deficit 是取整后的订单数量,而不是简单相减的结果)下一笔转账需要更多能量。
问题address.trx.lowtrx_balance_sun、needed_sun:整数 SUN;reason:bandwidth 或 multisigTRX 不足以支付带宽或多签费用。
问题address.trx.burnedtrx_burned_sun:整数 SUN;energy_used:整数能量;would_cost_sun:可为空的整数 SUN地址的一笔交易燃烧了 TRX 来获取能量。
问题address.tx.failedreason:out_of_energy 或 reverted;fee_limit_sun:可为空的整数 SUN;energy_required:可为空的整数能量(reverted 时两者均为 null)交易失败;回退不一定是能量不足导致的。
问题address.usdt.lowbalance、threshold:六位小数的 USDT 字符串USDT 跌破配置的余额偏低阈值。
安全address.permissions.changedowner_before、owner_after:可为空的 {threshold: integer, keys: [{address: Base58Check, weight: integer}]};active_changed:可为空的布尔值所有者/Active 权限已变更。
安全address.usdt.approvalspender:Base58Check;amount:六位小数的 USDT 字符串;unlimited:布尔值USDT 授权额度已更新(原始值 ≥ 2²⁵⁵ 时为 unlimited)。
安全address.usdt.frozenfrozen:布尔值发行方冻结或解冻了 USDT。
安全address.poisoning.suspectedlookalike_of、counterparty:Base58Check;direction:in 或 out;asset:USDT、TRX 或 null;token:可为空的 Base58Check 合约地址;amount:可为空的六位小数字符串,单位为已知资产历史记录中出现仿冒的交易对手地址。

对于关闭了能量警告(energy_alerts: false)的仅发送提醒场景中的地址,不会发送 address.energy.insufficient 和 address.trx.low。Webhook 信封中的 id 是投递去重键;data.event_id 在重放 API 中标识该 Radar 信号。负载示例、重放与确认状态的处理方式见 TRONAgg Radar 开发者指南。安全变更会在发生后第一时间报告。繁忙的 Telegram 聊天可能会收到一条文本形式的 radar.digest 汇总,代替单条非优先通知;它不是 Webhook 事件。权限变更、发行方冻结和无限额 USDT 授权从不合并。

order.status_changed​

轮询 GET /order/{id} 之外的推送方案。订单进入终态时触发一次。

{
"order_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"status": "COMPLETED",
"resource_type": "ENERGY",
"resource_amount": "65000",
"receiver_address": "TC7UXt7t2WLGbFasqvErydRQZ7bnGbQs",
"total_price_sun": 3500000,
"error_message": null
}
字段含义
statusCOMPLETED 或 FAILED(大写,与 API 一致)。
resource_amount订单 resource_type(ENERGY 或 BANDWIDTH)对应的数量,字符串。
total_price_sun向你收取的金额,单位 SUN。整笔能量订单由 Energy Pass 覆盖时为 0。
error_message仅当 status 为 FAILED 时有值。

balance.credited​

当一笔充值(entry_type: deposit)或一笔从分销商余额转入的款项(reseller_transfer)记入余额时触发。退款和其他入账只出现在 balance.changed 中。

{
"entry_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"entry_type": "deposit",
"amount_sun": 100000000,
"balance_sun": 250000000,
"reference_id": "0199a1b0-7c2e-7d41-9f3a-5b6c7d8e9f01"
}
字段含义
amount_sun入账金额(SUN)。
balance_sun入账后你的余额(SUN)。
entry_type链上充值为 deposit;从分销商余额转入的款项为 reseller_transfer(白标机器人的充值,或你自己从机器人余额转出的款项)。
reference_id对于 deposit,是充值 id(与 GET /deposits 中一致)。其他取值请视为不透明。

balance.changed​

完整的余额流水:每一笔余额变动都会触发——购买、充值、退款、提现、AutoEnergy 扣费、Energy Pass 付款等——并附上每次变动后的余额。高频账户每笔操作都会收到一个事件;除非你需要持续对账,否则请保持关闭。完全由 Energy Pass 覆盖的购买不会变动余额,也不会发送事件。

{
"entry_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"entry_type": "purchase",
"amount_sun": -2210000,
"balance_sun": 33196070000,
"reference_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b"
}
字段含义
amount_sun带符号金额(SUN):扣款为负,入账为正。
balance_sun本次变动后你的余额(SUN)。
entry_type变动类型,例如 purchase、refund、deposit、withdrawal、withdrawal_reversal、energy_pass、autoenergy_activate、autoenergy_daily、autoenergy_unit、autoenergy_penalty。请视为开放集合。
reference_id引起变动的对象 id:deposit 为充值 id(与 GET /deposits 中一致),purchase 和 refund 为订单 id(与 GET /order/{id} 中一致)。其他条目类型使用其他格式——请视为不透明。可能为 null。
threshold_sun仅当同一笔变动也触发了你的低余额规则时出现——见 balance.low。

autoenergy.delegated​

已订阅地址上每覆盖一笔转账触发一次。

{
"subscription_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"address": "TC7UXt7t2WLGbFasqvErydRQZ7bnGbQs",
"energy_class": "131k",
"billed_sun": 2100000,
"used_tx_id": "a1b2c3d4e5f6...",
"tx_used_total": 5,
"quota_max_tx": 100
}
字段含义
energy_class该笔转账覆盖的能量——65k 或 131k。
billed_sun为此计费的金额(SUN)。
used_tx_id消耗该能量的转账。
tx_used_total / quota_max_tx迄今已覆盖的转账笔数 / 配额(null = 不设上限)。

autoenergy.closed​

当 TRONAgg 自动停止一个订阅时触发——配额用尽、到期、处于空闲,或你的余额已无法覆盖它。你自己关闭订阅(通过 API、工作台或 Telegram 机器人)不会发送此事件。

{
"subscription_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"address": "TC7UXt7t2WLGbFasqvErydRQZ7bnGbQs",
"close_reason": "quota",
"tx_used_total": 42
}
字段含义
close_reason停止原因——quota、expired、idle、insufficient_balance、reseller_insolvent(某个白标机器人的服务被停止)等。请视为开放集合。
tx_used_total该订阅累计覆盖的转账笔数。

autoenergy.activated​

订阅在某个地址上生效时触发。

{
"subscription_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"address": "TC7UXt7t2WLGbFasqvErydRQZ7bnGbQs"
}

agent.sale​

当发往你某个 Energy Agent 配置的付款被转换为面向付款方的能量订单时触发。

{
"config_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"public_id": "k7Qm2xPa",
"title": "Night Shift Energy",
"payer": "TLa2f6VPqDgRE67v1736s7bJ8Ray5wYjU7",
"amount_sun": 6000000,
"units": 2,
"energy": 130000,
"cost_sun": 3900000,
"order_id": "0199a1b2-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
"tx_hash": "8e1a4f2c7b3d..."
}
字段含义
payer付款并接收能量的客户地址。
amount_sun客户支付的金额(SUN)。
units / energy计入的基础付款份数 / 订购的能量。
cost_sun从你的余额扣取的金额(SUN)。利润为 amount_sun − cost_sun。
order_id对应的能量订单;其结果通过 order.status_changed 送达。
tx_hash客户的付款交易。

agent.payment_skipped​

当一笔 Energy Agent 付款无法转换为能量时触发。不会从你的余额扣取任何费用。同一配置、同一原因每小时最多发送一次;GET /agent/payments 会逐笔列出被跳过的付款。

{
"config_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"public_id": "k7Qm2xPa",
"title": "Night Shift Energy",
"payer": "TLa2f6VPqDgRE67v1736s7bJ8Ray5wYjU7",
"amount_sun": 3000000,
"reason": "insufficient_balance",
"paused": true,
"tx_hash": "3b7d9e0f1a2c..."
}
字段含义
reasonbelow_base、price_above_payment、self_transfer、insufficient_balance、config_paused、program_disabled、order_rejected 等。请视为开放集合;参见被跳过的付款。
paused当这笔付款同时因余额不足导致你的配置暂停时为 true。

energy_pass.renewed​

当你的 Energy Pass 在周期结束时续订、并从你的余额扣取套餐价格时触发。

{
"plan_name": "Pro",
"daily_energy": 650000,
"price_sun": 90000000,
"period_end": "2026-08-11T10:30:00.123456+00:00"
}
字段含义
plan_name续订的套餐。
daily_energy新周期的每日能量额度。
price_sun为新周期扣取的金额(SUN)。
period_end新周期的结束时间(带 UTC 偏移的 ISO-8601)。

energy_pass.expired​

当你的 Energy Pass 在周期结束时终止而没有续订时触发。

{
"plan_name": "Pro",
"reason": "insufficient_balance",
"price_sun": 90000000,
"balance_sun": 41000000
}
字段含义
reasonauto_renew_off(你已取消)、insufficient_balance(你的余额不足以支付续订)、plan_unavailable(该套餐已不再提供)、program_disabled(Energy Pass 目前不可用)、account_blocked。请视为开放集合。
price_sun现在续订该套餐的费用(SUN);套餐不再提供时为 null。
balance_sun续订失败时你的余额(SUN)。仅当 reason 为 insufficient_balance 时有值,否则为 null。

vault.payout.sent​

当为你登录所用的、或已在工作台中验证的委托地址发出一笔能量质押结算时触发。

{
"payout_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"delegator_address": "TC7UXt7t2WLGbFasqvErydRQZ7bnGbQs",
"amount_sun": 12500000,
"tx_id": "5c9e2a7f1b3d..."
}
字段含义
delegator_address该笔结算所属的委托地址。
amount_sun结算金额(SUN)。
tx_id链上结算交易。

balance.low​

当一笔余额变动使你的余额低于阈值时触发。在你的余额回升到阈值或以上之前,不会再次触发。默认阈值:50 TRX(50000000 SUN);可在 工作台 → 通知 中修改。

如果同一个 Webhook 上也开启了 balance.changed,该提醒会以设置了 threshold_sun 的那条 balance.changed 事件送达,而不是单独的 balance.low。

{
"entry_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"entry_type": "purchase",
"amount_sun": -3500000,
"balance_sun": 12000000,
"reference_id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"threshold_sun": 50000000
}
字段含义
balance_sun跌破阈值的那笔变动之后的余额(SUN)。
threshold_sun你配置的阈值(SUN)。
amount_sun该笔变动本身——扣款为负数。

其他字段的含义与 balance.changed 中相同。

account.daily_summary​

每天在你选择的整点发送一次(默认 09:00 UTC)。

{
"date": "2026-07-23",
"completed_orders": 12,
"energy_received": "780000",
"spent_sun": 42000000,
"balance_sun": 250000000
}
字段含义
date你所选时区中的报告日期。
completed_orders报告时刻之前 24 小时内完成的订单数。
energy_received这些订单的能量总量(字符串)。
spent_sun这些订单的费用(SUN)。
balance_sun生成报告时你的余额(SUN)。

分销商事件​

如果你的账户运营着一个分销机器人,那么在你成为分销商之后创建的每个 Webhook 还会获得 reseller 订阅。这些事件带有 "audience": "reseller"。

事件触发时机默认
order.status_changed通过你的机器人下的订单完结。✅ On
reseller.balance.low你的分销商余额跌破你设置的阈值(默认 500 TRX)。✅ On
reseller.bot_status_changed你的机器人被激活或暂停,或健康检查发现它不可用或已恢复。✅ On
reseller.autoenergy.insolvent你的分销商余额无法覆盖你低于成本的 AutoEnergy 定价:你的机器人中新的 AutoEnergy 连接被阻止,活跃订阅被停止。每天最多一次。✅ On
reseller.daily_summary你的机器人销售情况的每日摘要。— Off

当你通过自己的机器人购买时,只会收到 reseller 版本的 order.status_changed,不会再收到一份 customer 版本。

order.status_changed(reseller 受众)的字段与客户事件相同,另加:

字段含义
total_price_sun你为该订单支付的成本(SUN),不含你的加价。
markup_sun你在该订单上的加价(SUN)。
customer买家的 Telegram @username 或名字(已知时)。

reseller.balance.low:entry_id、entry_type、amount_sun、balance_sun、reference_id 和 threshold_sun。含义与 balance.low 相同,针对的是你的分销商余额。

reseller.bot_status_changed:

字段含义
bot_id / bot_username该机器人。
sourceoperator——机器人被激活或暂停(status 为 active / paused)。monitor——健康检查发现它 unavailable 或 recovered;这类事件还带有一个诊断用的 details 对象。
status / previous_status新状态与之前的状态。两者都请视为开放集合。

reseller.autoenergy.insolvent:仅含 reseller_id。

reseller.daily_summary(在你选择的整点发送,默认 09:00 UTC;覆盖此前 24 小时):

字段含义
date你所选时区中的报告日期。
orders / completed / failed通过你的机器人下的订单数,以及其中完成和失败的数量。
turnover_sun你的客户支付的金额(SUN)。
revenue_sun你赚取的加价(SUN)。
new_users你的机器人中的新客户数。

测试你的端点​

POST /webhooks/{id}/test 会发送一条 notification.test 事件,签名方式与真实投递完全相同——用它端到端确认你的端点和签名检查。它没有 audience 字段。每个账户每小时最多可发送 5 次测试,与工作台中的 发送测试 按钮共用。

{
"id": "0199a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"type": "notification.test",
"created_at": "2026-07-12T10:30:00.123456+00:00",
"data": { "message": "TRONAgg notifications are connected." }
}

下一步​