Skip to main content

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 set detail to a bare string instead of an object. These are:

    • 401 on any API-key endpoint"API key required", "Invalid API key", or "User not found".
    • 404 on GET /order/{id}"Order not found" (also returned when the order exists but is not yours).
    • 400 on POST /webhooks when 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, not error. The order-amount-limit errors on POST /buy use code (plus min / max) — see Order amount limits.

  • Request-validation 422. A malformed request body or an out-of-range query parameter (for example resource_amount ≤ 0, or page_size above 100) is rejected by the framework with 422 and a detail array 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 codeHTTPEndpoint(s)Extra fieldsWhen
unsupported_resource400GET /estimate, POST /buyresource_type other than ENERGY.
invalid_duration400GET /estimate, POST /buyduration other than 1h.
invalid_idempotency_key400POST /buyIdempotency-Key longer than 128 characters.
insufficient_balance400POST /buycurrent_balance, required_amountBalance below the order total. No message field on this endpoint.
validation_error400POST /buy, POST /webhooksmessagePurchase rejected on validation, or a rejected webhook URL (e.g. not HTTPS).
invalid_cursor400GET /transactionsmessageThe cursor value could not be parsed.
duplicate_request409POST /buyA request with the same Idempotency-Key is still in flight.
not_found404DELETE /webhooks/{id}, POST /webhooks/{id}/testmessageNo such webhook for this account.
delivery_failed502POST /webhooks/{id}/testmessageThe test delivery did not succeed.
service_unavailable503GET /estimate, POST /buymessageEnergy purchasing is temporarily unavailable.
internal_error500POST /buymessageUnexpected 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 codeHTTPWhen
plan_not_found400The requested plan does not exist.
insufficient_balance400Balance too low to start or continue the subscription. (Carries message here, unlike POST /buy.)
not_found404No such subscription for this account.
address_busy409The address is currently being processed; retry shortly.
address_exists409An active subscription already covers this address.
not_reactivatable409The subscription cannot be reactivated in its current state.
not_deletable409The subscription cannot be deleted in its current state.
invalid_address422The address is not a valid TRON address.
fulfiller_unavailable502The fulfilment backend could not be reached; retry.
autoenergy_temporarily_unavailable503AutoEnergy is temporarily unavailable; retry later.
internal_error500Unexpected 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.

EndpointRequest paramsResponse fieldsStyle
GET /orderspage (≥ 1), page_size (1–100)orders, total, page, page_sizePage-numbered, with a full total count.
GET /transactionslimit (1–100), cursortransactions, has_more, next_cursorCursor-based. Pass the previous next_cursor back as cursor to get the next page; stop when has_more is false.
GET /depositslimit (1–100), offset (≥ 0)deposits, has_moreLimit/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 *_sun fields 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 status is one of PENDING, PROCESSING, AWAITING_PAYMENT, COMPLETED, or FAILED. COMPLETED and FAILED are terminal.
  • TRON addresses. Addresses are base58 strings that start with T and 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-Key header and the 401 responses above.
  • Buy energy — the purchase endpoint that most of these errors come from.
  • Webhook events — the push alternative to polling for order status.