Errors & conventions
This page is the reference for how the TRONAgg API reports failures and the cross-cutting rules every endpoint follows: the error envelope, the full error-code catalog, idempotency on purchases, the pagination styles, and the units and formats you will see in every response.
The error envelope
Errors use the standard HTTP status code plus a JSON body with a top-level detail. Most endpoints put a structured object in detail: a machine-readable error code, a human-readable message, and — for a few errors — extra context fields.
{
"detail": {
"error": "insufficient_balance",
"current_balance": 1200000,
"required_amount": 3500000
}
}
Branch on detail.error; show detail.message to a human when present.
Honest exceptions
A few responses do not follow the object-with-error shape. Handle them defensively — read detail, and:
-
Plain-string
detail. Some responses setdetailto a bare string instead of an object. These are:401on any API-key endpoint —"API key required","Invalid API key", or"User not found".404onGET /order/{id}—"Order not found"(also returned when the order exists but is not yours).400onPOST /webhookswhen the URL host is not a public host — e.g."Webhook URL must use a public host","Webhook URL must include a host","Webhook host could not be resolved".
{ "detail": "Order not found" } -
Object keyed by
code, noterror. The order-amount-limit errors onPOST /buyusecode(plusmin/max) — see Order amount limits. -
Request-validation
422. A malformed request body or an out-of-range query parameter (for exampleresource_amount≤ 0, orpage_sizeabove 100) is rejected by the framework with422and adetailarray of field errors.{ "detail": [ { "loc": ["query", "resource_amount"], "msg": "...", "type": "..." } ] }
A robust client therefore checks the type of detail: object → branch on detail.error (falling back to detail.code); string → treat as a human message; array → request-validation error.
Error codes
Programmatic API
error code | HTTP | Endpoint(s) | Extra fields | When |
|---|---|---|---|---|
unsupported_resource | 400 | GET /estimate, POST /buy | — | resource_type other than ENERGY. |
invalid_duration | 400 | GET /estimate, POST /buy | — | duration other than 1h. |
invalid_idempotency_key | 400 | POST /buy | — | Idempotency-Key longer than 128 characters. |
insufficient_balance | 400 | POST /buy | current_balance, required_amount | Balance below the order total. No message field on this endpoint. |
validation_error | 400 | POST /buy, POST /webhooks | message | Purchase rejected on validation, or a rejected webhook URL (e.g. not HTTPS). |
invalid_cursor | 400 | GET /transactions | message | The cursor value could not be parsed. |
duplicate_request | 409 | POST /buy | — | A request with the same Idempotency-Key is still in flight. |
not_found | 404 | DELETE /webhooks/{id}, POST /webhooks/{id}/test | message | No such webhook for this account. |
delivery_failed | 502 | POST /webhooks/{id}/test | message | The test delivery did not succeed. |
service_unavailable | 503 | GET /estimate, POST /buy | message | Energy purchasing is temporarily unavailable. |
internal_error | 500 | POST /buy | message | Unexpected server error (captured for investigation). Retry later. |
For the order_amount_below_minimum / order_amount_above_maximum codes on POST /buy, see Order amount limits.
AutoEnergy
The AutoEnergy endpoints (/autoenergy/*) all use the object envelope — {"error": <code>, "message": <text>} — with these codes:
error code | HTTP | When |
|---|---|---|
plan_not_found | 400 | The requested plan does not exist. |
insufficient_balance | 400 | Balance too low to start or continue the subscription. (Carries message here, unlike POST /buy.) |
not_found | 404 | No such subscription for this account. |
address_busy | 409 | The address is currently being processed; retry shortly. |
address_exists | 409 | An active subscription already covers this address. |
not_reactivatable | 409 | The subscription cannot be reactivated in its current state. |
not_deletable | 409 | The subscription cannot be deleted in its current state. |
invalid_address | 422 | The address is not a valid TRON address. |
fulfiller_unavailable | 502 | The fulfilment backend could not be reached; retry. |
autoenergy_temporarily_unavailable | 503 | AutoEnergy is temporarily unavailable; retry later. |
internal_error | 500 | Unexpected server error (captured for investigation). Retry later. |
Idempotency
POST /buy is the only money-moving write, so it accepts an Idempotency-Key header to make retries safe:
- Send any unique string of ≤ 128 characters (a UUID is a good choice). A longer key returns
400 invalid_idempotency_key. - The first request with a given key is processed; its response is cached for 24 hours and returned verbatim for any later request that reuses the same key — so a network retry never places a second order.
- If a duplicate arrives while the first is still being processed, it returns
409 duplicate_request. Wait and retry; you will then get the cached original response. - Keys are scoped per account. Reusing a key across two genuinely different purchases returns the first purchase's response, not a new order.
The header is optional but strongly recommended for any automated buyer.
Pagination
Three list endpoints exist, and — for historical reasons — each paginates differently. Read each on its own terms.
| Endpoint | Request params | Response fields | Style |
|---|---|---|---|
GET /orders | page (≥ 1), page_size (1–100) | orders, total, page, page_size | Page-numbered, with a full total count. |
GET /transactions | limit (1–100), cursor | transactions, has_more, next_cursor | Cursor-based. Pass the previous next_cursor back as cursor to get the next page; stop when has_more is false. |
GET /deposits | limit (1–100), offset (≥ 0) | deposits, has_more | Limit/offset. Advance offset by limit each page; stop when has_more is false. |
All three return newest-first. There is no total on /transactions or /deposits — use has_more to decide whether to fetch another page.
The receiver_address filter on GET /orders
The optional receiver_address query parameter is a substring match, applied to both the receiver and the buyer address of each order (case-insensitive). A partial value matches any order whose receiver or buyer address contains it; a full 34-character address behaves as an exact match.
Units & formats
- SUN. All amounts in
*_sunfields are integers denominated in SUN, the smallest TRX unit: 1 TRX = 1,000,000 SUN. - Decimal amounts are strings. Human-facing TRX amounts (
total_price_trx,amount_trx,balance_trx, …) are serialized as JSON strings, not numbers, to preserve exact precision. Parse them with a decimal type, never a float. - Order statuses are upper-case. An order's
statusis one ofPENDING,PROCESSING,AWAITING_PAYMENT,COMPLETED, orFAILED.COMPLETEDandFAILEDare terminal. - TRON addresses. Addresses are base58 strings that start with
Tand are 34 characters long. - Timestamps. Time fields (
created_at,completed_at, …) are ISO-8601 in UTC.
Order amount limits
Every order has a minimum and maximum resource amount. Read the current bounds from GET /estimate, which returns min_amount and max_amount alongside the price.
POST /buy enforces the same bounds. An out-of-range amount returns 400 with a structured detail that — unlike the other purchase errors — is keyed by code (not error) and carries min / max:
{
"detail": {
"code": "order_amount_below_minimum",
"message": "Order amount 500 is below the minimum (1000). Increase the amount or contact support.",
"min": 1000,
"max": 5000000
}
}
The two codes are order_amount_below_minimum and order_amount_above_maximum. Check GET /estimate first to avoid the round-trip.
Next steps
- Authentication — the
X-API-Keyheader and the401responses above. - Buy energy — the purchase endpoint that most of these errors come from.
- Webhook events — the push alternative to polling for order status.