Docs menu

Account — API reference

Who am I, what is my balance, where do I send money.

MethodPathSummary
GET/v1/accountAccount information
PATCH/v1/accountEdit the account’s display name
GET/v1/balanceBalances only
GET/v1/deposit-addressesDeposit addresses for this account
GET/v1/withdrawalsThe account’s withdrawals, newest first
POST/v1/withdrawalsWithdraw the balance to the owner wallet
GET/v1/withdrawals/sourcesThe wallets a withdrawal can go to, and how much each can take
GET/v1/ledgerThe account’s ledger statement
GET/v1/account/feeThe service fee this account pays
POST/v1/account/promoApply a promo code
GET/v1/account/addressesThe account’s address book
POST/v1/account/addressesSave an address
DELETE/v1/account/addresses/{entryId}Remove an address
GET/v1/account/statsOrder analytics, aggregated server-side

Base URL https://api.tenergy.me/v1, or https://api-nile.tenergy.me/v1 on Nile (Environments). Every request below is signed as in Authentication unless its Auth line says otherwise.

Account information

GET /v1/account · getAccount

Auth: API key (HMAC) or bootstrap token.

Identity, balances, deposit addresses, the limits in force and lifetime counters.

Also readable with a bootstrap token (Authorization: Bearer abt_…), so the signup flow can poll for the first confirmed deposit without an API key.

Responses

StatusMeaning
200OK
401Missing, malformed or rejected credentials.
429Too many requests.
500Something broke on our side.

Response fields (Account)

FieldTypeRequiredDescription
idstringyes
brandstringyesThe brand this account belongs to. Accounts are not shared across brands.
networkenum: mainnet, nileyesThe environment this account belongs to. An account, its ledger and its keys do not span environments: a Nile account is a separate account on the api-nile.<brand-domain> host.
owner_addressstring | nullThe address that signed the challenge this account was created with; the root of recovery. null only for legacy accounts created before the challenge model.
labelstring | nullThe same value as display_name, under the name this field has always had. New clients should read display_name; this one is not going away.
display_namestring | nullThe account’s own title, chosen by its owner and editable with PATCH /v1/account. null when it has none, in which case a UI shows the shortened owner_address instead of an internal id.
statusenum: unfunded, active, suspended, closedyes
balance_suninteger (int64)yesAn amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
balance_usdtinteger (int64)USDT balance in the token’s smallest unit (6 decimals).
reserved_suninteger (int64)Held against in-flight orders. Not spendable.
deposit_addressesarray of object (DepositAddress)yes
deposit_addresses[].currencyenum: TRX, USDTyes
deposit_addresses[].addressstringyesBase58Check TRON address (starts with T, 34 characters).
deposit_addresses[].memostring | nullAlways null since 2026-09-26: every account has an address of its own, so no memo is needed and any memo is ignored. Kept so existing clients do not break.
deposit_addresses[].confirmations_requiredintegerThe most block confirmations a deposit to this address waits before it is credited. A TRX deposit may wait fewer by its amount: see confirmation_tiers.
deposit_addresses[].confirmation_tiersarray of objectTRX row only. The confirmations a TRX deposit waits by its amount, ascending: a deposit takes the first row whose up_to_trx it does not exceed; the last row (up_to_trx: null) is everything above. Counted from sending the payment, the wait is about 3 s to get into a block plus 3 s per confirmation. Direct purchases (a transfer to a brand address) follow the same rows.
deposit_addresses[].confirmation_tiers[].up_to_trxstring | nullyesTRX, inclusive; null on the last row.
deposit_addresses[].confirmation_tiers[].confirmationsintegeryes
deposit_addresses[].contractstring | nullThe TRC-20 contract accepted at this address (USDT row only; null for TRX).
deposit_addresses[].rate_nownull | objectUSDT row only. The current SunSwap (or fixed) TRX-per-USDT rate BEFORE the spread, cached 60 s. null when no rate is available right now; the deposit is still accepted and the worker prices it at credit time. Indicative: the credit uses the rate the worker reads when it credits, not this one.
deposit_addresses[].rate_now.trx_per_usdtstringyes
deposit_addresses[].rate_now.sourceenum: sunswap_v3, fixed_envyes
deposit_addresses[].rate_now.atstring (date-time)yes
deposit_addresses[].spread_bpsinteger | nullUSDT row only: basis points taken off the rate (5 = 0.05 %).
deposit_addresses[].minstring | nullUSDT row only: smallest credited USDT deposit; smaller ones are held (held_below_min).
deposit_addresses[].maxstring | nullUSDT row only: largest auto-credited USDT deposit; larger ones are held (held_for_review).
limitsobject
limits.max_order_energyinteger
limits.max_batch_receiversinteger
limits.orders_per_secondinteger
totalsobjectLifetime counters. Informational; not a substitute for the ledger.
totals.deposited_suninteger (int64)An amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
totals.spent_suninteger (int64)An amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
totals.refunded_suninteger (int64)An amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
totals.orders_createdinteger
totals.energy_delegatedinteger (int64)
created_atstring (date-time)yes

Example 200 response, from the contract (illustrative values — live numbers come from the API):

json
{
  "id": "acc_01J9Z4K2M7Q8",
  "brand": "tenergy.me",
  "network": "mainnet",
  "label": "Acme payments",
  "status": "active",
  "balance_sun": 1250400000,
  "balance_usdt": 0,
  "reserved_sun": 0,
  "deposit_addresses": [{"currency":"TRX","address":"TDepositAddressExample1111111111111"}],
  "limits": {"max_order_energy":3000000,"max_batch_receivers":100,"orders_per_second":30},
  "totals": {
    "deposited_sun": 5000000000,
    "spent_sun": 3749600000,
    "refunded_sun": 0,
    "orders_created": 1842,
    "energy_delegated": 119730000
  },
  "created_at": "2026-04-02T09:11:00.000Z"
}

Edit the account’s display name

PATCH /v1/account · updateAccount

Auth: Dashboard session cookie + X-CSRF-Token.

The account’s own title, shown wherever the account is named. null clears it and the display falls back to the shortened owner address — never to an internal id.

A dashboard session operation, and the account’s owner: the title is what every member sees and what support reads back in a ticket. It is a setting; nothing about who may reach what changes here.

Request body

JSON (AccountPatch), required.

FieldTypeRequiredDescription
display_namestring | nullThe account’s title. Trimmed; must not be blank and must carry no control characters. null clears it.

Responses

StatusMeaning
200Updated
400Malformed request — bad JSON, unknown field, wrong type.
401Missing, malformed or rejected credentials.
403Authenticated, but this key is not allowed to do it.
422Syntactically valid but semantically impossible.
500Something broke on our side.

Response fields (Account)

FieldTypeRequiredDescription
idstringyes
brandstringyesThe brand this account belongs to. Accounts are not shared across brands.
networkenum: mainnet, nileyesThe environment this account belongs to. An account, its ledger and its keys do not span environments: a Nile account is a separate account on the api-nile.<brand-domain> host.
owner_addressstring | nullThe address that signed the challenge this account was created with; the root of recovery. null only for legacy accounts created before the challenge model.
labelstring | nullThe same value as display_name, under the name this field has always had. New clients should read display_name; this one is not going away.
display_namestring | nullThe account’s own title, chosen by its owner and editable with PATCH /v1/account. null when it has none, in which case a UI shows the shortened owner_address instead of an internal id.
statusenum: unfunded, active, suspended, closedyes
balance_suninteger (int64)yesAn amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
balance_usdtinteger (int64)USDT balance in the token’s smallest unit (6 decimals).
reserved_suninteger (int64)Held against in-flight orders. Not spendable.
deposit_addressesarray of object (DepositAddress)yes
deposit_addresses[].currencyenum: TRX, USDTyes
deposit_addresses[].addressstringyesBase58Check TRON address (starts with T, 34 characters).
deposit_addresses[].memostring | nullAlways null since 2026-09-26: every account has an address of its own, so no memo is needed and any memo is ignored. Kept so existing clients do not break.
deposit_addresses[].confirmations_requiredintegerThe most block confirmations a deposit to this address waits before it is credited. A TRX deposit may wait fewer by its amount: see confirmation_tiers.
deposit_addresses[].confirmation_tiersarray of objectTRX row only. The confirmations a TRX deposit waits by its amount, ascending: a deposit takes the first row whose up_to_trx it does not exceed; the last row (up_to_trx: null) is everything above. Counted from sending the payment, the wait is about 3 s to get into a block plus 3 s per confirmation. Direct purchases (a transfer to a brand address) follow the same rows.
deposit_addresses[].confirmation_tiers[].up_to_trxstring | nullyesTRX, inclusive; null on the last row.
deposit_addresses[].confirmation_tiers[].confirmationsintegeryes
deposit_addresses[].contractstring | nullThe TRC-20 contract accepted at this address (USDT row only; null for TRX).
deposit_addresses[].rate_nownull | objectUSDT row only. The current SunSwap (or fixed) TRX-per-USDT rate BEFORE the spread, cached 60 s. null when no rate is available right now; the deposit is still accepted and the worker prices it at credit time. Indicative: the credit uses the rate the worker reads when it credits, not this one.
deposit_addresses[].rate_now.trx_per_usdtstringyes
deposit_addresses[].rate_now.sourceenum: sunswap_v3, fixed_envyes
deposit_addresses[].rate_now.atstring (date-time)yes
deposit_addresses[].spread_bpsinteger | nullUSDT row only: basis points taken off the rate (5 = 0.05 %).
deposit_addresses[].minstring | nullUSDT row only: smallest credited USDT deposit; smaller ones are held (held_below_min).
deposit_addresses[].maxstring | nullUSDT row only: largest auto-credited USDT deposit; larger ones are held (held_for_review).
limitsobject
limits.max_order_energyinteger
limits.max_batch_receiversinteger
limits.orders_per_secondinteger
totalsobjectLifetime counters. Informational; not a substitute for the ledger.
totals.deposited_suninteger (int64)An amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
totals.spent_suninteger (int64)An amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
totals.refunded_suninteger (int64)An amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
totals.orders_createdinteger
totals.energy_delegatedinteger (int64)
created_atstring (date-time)yes

Balances only

GET /v1/balance · getBalance

Auth: API key (HMAC).

A deliberately small response for callers that poll before every order. Cheaper than /account and on the read rate-limit budget.

Responses

StatusMeaning
200OK
401Missing, malformed or rejected credentials.
429Too many requests.
500Something broke on our side.

Response fields (Balance)

FieldTypeRequiredDescription
balance_suninteger (int64)yesAn amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
balance_usdtinteger (int64)
reserved_suninteger (int64)An amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
available_suninteger (int64)yesbalance_sun - reserved_sun. This is what an order can draw on.
as_ofstring (date-time)yes

Example 200 response, from the contract (illustrative values — live numbers come from the API):

json
{
  "balance_sun": 1250400000,
  "balance_usdt": 0,
  "reserved_sun": 0,
  "available_sun": 1250400000,
  "as_of": "2026-09-11T18:04:05.123Z"
}

Deposit addresses for this account

GET /v1/deposit-addresses · listDepositAddresses

Auth: API key (HMAC).

The account’s own deposit address: a TRON address that belongs to this account alone, so no memo is needed (memo is always null). Two rows, same address: TRX, and USDT (TRC-20, contract), which the ledger credits as TRX at rate_now minus spread_bps, between min and max USDT. The address is issued on the first read if the account has none yet.

A TRX deposit is credited after block confirmations that depend on its amount (confirmation_tiers); the wait is counted from sending the payment: about 3 s for the transaction to get into a block, then 3 s per confirmation.

TRX amountConfirmationsApproximate wait
up to 2001~6 s
over 200, up to 1,0002~9 s
over 1,000, up to 5,0003~12 s
over 5,00019~1 min

A USDT deposit waits 19 confirmations (~1 min). confirmations_required is the most a deposit waits. Every credit produces a balance.credited webhook.

Support can replace the address (never the customer; at most once per rotation.min_interval_days and rotation.max_per_window times per rotation.window_days). A replaced address is listed under retired and still credits until credited_until (rotation.retired_credit_days after it was retired).

Responses

StatusMeaning
200OK
401Missing, malformed or rejected credentials.
429Too many requests.
500Something broke on our side.

Response fields

FieldTypeRequiredDescription
dataarray of object (DepositAddress)yes
data[].currencyenum: TRX, USDTyes
data[].addressstringyesBase58Check TRON address (starts with T, 34 characters).
data[].memostring | nullAlways null since 2026-09-26: every account has an address of its own, so no memo is needed and any memo is ignored. Kept so existing clients do not break.
data[].confirmations_requiredintegerThe most block confirmations a deposit to this address waits before it is credited. A TRX deposit may wait fewer by its amount: see confirmation_tiers.
data[].confirmation_tiersarray of objectTRX row only. The confirmations a TRX deposit waits by its amount, ascending: a deposit takes the first row whose up_to_trx it does not exceed; the last row (up_to_trx: null) is everything above. Counted from sending the payment, the wait is about 3 s to get into a block plus 3 s per confirmation. Direct purchases (a transfer to a brand address) follow the same rows.
data[].confirmation_tiers[].up_to_trxstring | nullyesTRX, inclusive; null on the last row.
data[].confirmation_tiers[].confirmationsintegeryes
data[].contractstring | nullThe TRC-20 contract accepted at this address (USDT row only; null for TRX).
data[].rate_nownull | objectUSDT row only. The current SunSwap (or fixed) TRX-per-USDT rate BEFORE the spread, cached 60 s. null when no rate is available right now; the deposit is still accepted and the worker prices it at credit time. Indicative: the credit uses the rate the worker reads when it credits, not this one.
data[].rate_now.trx_per_usdtstringyes
data[].rate_now.sourceenum: sunswap_v3, fixed_envyes
data[].rate_now.atstring (date-time)yes
data[].spread_bpsinteger | nullUSDT row only: basis points taken off the rate (5 = 0.05 %).
data[].minstring | nullUSDT row only: smallest credited USDT deposit; smaller ones are held (held_below_min).
data[].maxstring | nullUSDT row only: largest auto-credited USDT deposit; larger ones are held (held_for_review).
retiredarray of objectAddresses this account used before, newest first.
retired[].addressstringyesBase58Check TRON address (starts with T, 34 characters).
retired[].retired_atstring (date-time)yes
retired[].credited_untilstring (date-time)yesA transfer to this address in a block after this instant is not credited automatically; contact support.
rotationobjectWho may replace the address, and how often.
rotation.byenum: supportyes
rotation.min_interval_daysintegeryes
rotation.max_per_windowintegeryes
rotation.window_daysintegeryes
rotation.retired_credit_daysintegeryes

Example 200 response, from the contract (illustrative values — live numbers come from the API):

json
{
  "data": [
    {
      "currency": "TRX",
      "address": "TDepositAddressExample1111111111111",
      "memo": null,
      "confirmations_required": 19
    }
  ],
  "retired": [
    {
      "address": "TRetiredAddressExample111111111111",
      "retired_at": "2026-09-20T10:00:00.000Z",
      "credited_until": "2026-10-20T10:00:00.000Z"
    }
  ],
  "rotation": {
    "by": "support",
    "min_interval_days": 7,
    "max_per_window": 3,
    "window_days": 90,
    "retired_credit_days": 30
  }
}

The account’s withdrawals, newest first

GET /v1/withdrawals · listWithdrawals

Auth: Dashboard session cookie + X-CSRF-Token.

Responses

StatusMeaning
200OK

Response fields

FieldTypeRequiredDescription
dataarray of object (Withdrawal)yes
data[].idstringyes
data[].statusenum: awaiting_approval, approved, sent, confirmed, rejected, failedyesA rejected or failed withdrawal is returned to the balance in full, fee included.
data[].to_addressstringyesThe wallet the money goes back to — one that topped the account up.
data[].amount_suninteger (int64)yes
data[].fee_suninteger (int64)yes
data[].txidstring | null
data[].created_atstring (date-time)yes
data[].sent_atstring (date-time) | null
data[].confirmed_atstring (date-time) | null

Withdraw the balance to the owner wallet

POST /v1/withdrawals · createWithdrawal

Auth: Dashboard session cookie + X-CSRF-Token.

Sends amount_sun from the account’s balance back to a wallet that topped it up — to_address, one of GET /v1/withdrawals/sources — and at most what that wallet paid in, less what already went back to it. Money received from other wallets buys energy but is not withdrawn to a wallet that did not send it (3024 withdrawal_destination_not_allowed). Without to_address: the owner address, or the only source. A flat fee (1 TRX) is taken on top, and the balance is debited at once. One withdrawal at a time: a second one while the first is open answers 3023 withdrawal_in_progress.

Most withdrawals are paid out automatically within a minute. A large one, or one above what the account has paid in, waits for a manual check first (awaiting_approval). Signed-in owner only; not available with an API key. With 2-step on, needs a confirmation bound to this amount and destination (1020 step_up_required).

Parameters

NameInTypeRequiredDescription
X-Step-UpheaderstringA 2-step approval: details.step_up_id of a 1020 step_up_required answer, approved with POST /v1/step-up/{id}/approve. Sent on the repeat of the identical request; used once. Only a dashboard session whose person switched 2-step on is ever asked for it.

Request body

JSON, required.

FieldTypeRequiredDescription
amount_suninteger (int64)yesWhat reaches the wallet
to_addressstringA wallet from GET /v1/withdrawals/sources.

Responses

StatusMeaning
201Created
4031020 step_up_required (2-step on: approve, then repeat with X-Step-Up), 1021 step_up_invalid, 1014 csrf_token_invalid.

Response fields (Withdrawal)

FieldTypeRequiredDescription
idstringyes
statusenum: awaiting_approval, approved, sent, confirmed, rejected, failedyesA rejected or failed withdrawal is returned to the balance in full, fee included.
to_addressstringyesThe wallet the money goes back to — one that topped the account up.
amount_suninteger (int64)yes
fee_suninteger (int64)yes
txidstring | null
created_atstring (date-time)yes
sent_atstring (date-time) | null
confirmed_atstring (date-time) | null

The wallets a withdrawal can go to, and how much each can take

GET /v1/withdrawals/sources · listWithdrawalSources

Auth: Dashboard session cookie + X-CSRF-Token.

Every wallet that paid into the account. available_sun is what may go back to it now: what it paid in, less what already went back to it, never more than the free balance.

Responses

StatusMeaning
200OK

Response fields

FieldTypeRequiredDescription
enabledbooleanyesWhether withdrawals are available at all.
dataarray of objectyes
data[].addressstringyes
data[].paid_in_suninteger (int64)yes
data[].withdrawn_suninteger (int64)yes
data[].available_suninteger (int64)yes

The account’s ledger statement

GET /v1/ledger · listLedger

Auth: Dashboard session cookie + X-CSRF-Token.

Every journal that moved this account’s balance, newest first: deposits, order charges, refunds and the rest. amount_sun / amount_usdt are the net effect on the balance — positive when it went up. kind=deposit is the deposit history of the top-up screen.

A deposit appears here once it is credited, i.e. after its confirmations_required block confirmations (by its amount for TRX: 1 up to 200 TRX, 2 up to 1,000, 3 up to 5,000, 19 above; 19 for USDT); a transfer still confirming is not listed yet. sender and block_number are null on deposits credited before 2026-09-25.

Dashboard session only (member role viewer or above). No API-key scope opens it: the ledger shows senders and transaction ids that no key-readable endpoint shows today, and extending balance.read to it is an access decision that has not been taken.

Parameters

NameInTypeRequiredDescription
kindqueryenum: deposit, all
limitqueryinteger
cursorquerystringOpaque cursor from a previous response’s next_cursor.

Responses

StatusMeaning
200OK
400Malformed request — bad JSON, unknown field, wrong type.
401Missing, malformed or rejected credentials.
403Authenticated, but this key is not allowed to do it.
500Something broke on our side.

Response fields

FieldTypeRequiredDescription
dataarray of object (LedgerEntry)yes
data[].idstringyesThe ledger journal id.
data[].kindenum: deposit, order_charge, order_refund, referral_payout, subscription_fee, subscription_usage, reserve_fee, invoice, withdrawal, adjustmentyes
data[].amount_suninteger (int64)yesNet effect on the TRX balance in SUN; negative for a charge.
data[].amount_usdtinteger (int64)yesNet effect on the USDT balance in the token’s smallest unit.
data[].order_idstring | nullyesThe order a charge or refund belongs to.
data[].depositnull | objectyesSet for kind = deposit, null otherwise.
data[].deposit.txidstring | nullyesThe deposit transaction hash.
data[].deposit.senderstring | nullyesThe address the TRX came from; null on deposits credited before 2026-09-25.
data[].deposit.block_numberinteger | nullyes
data[].deposit.statusenum: credited, pending_rate, held_below_min, held_for_reviewyescredited for every journal. With kind=deposit, USDT deposits the worker saw but did not credit are listed too (first page only): held_below_min, held_for_review or pending_rate, with amount_sun 0 — nothing was credited.
data[].deposit.confirmations_requiredintegeryesThe block confirmations this deposit waited before it was credited (by its amount for TRX).
data[].deposit.assetenum: TRX, USDT
data[].deposit.usdt_amountstring | nullUSDT received (decimal string); null for TRX.
data[].deposit.ratestring | nullSUN credited per 1 USDT, after the spread; null for TRX and held rows.
data[].deposit.rate_sourcestring | nullWhere the rate came from (fixed_env, sunswap_v3@<block>, …); null for TRX.
data[].deposit.spread_bpsinteger | null
data[].created_atstring (date-time)yes
next_cursorstring | nullyes

Example 200 response, from the contract (illustrative values — live numbers come from the API):

json
{
  "data": [
    {
      "id": "jrn_01K5Y8Q2V9M3ZP4T7R1A2B3C4D",
      "kind": "order_charge",
      "amount_sun": -3900000,
      "amount_usdt": 0,
      "order_id": "ord_01K5Y8Q2V9M3ZP4T7R1A2B3C4E",
      "deposit": null,
      "created_at": "2026-09-25T10:12:04.000Z"
    },
    {
      "id": "jrn_01K5Y7A1B2C3D4E5F6G7H8J9K0",
      "kind": "deposit",
      "amount_sun": 50000000,
      "amount_usdt": 0,
      "order_id": null,
      "deposit": {
        "txid": "9f3c1e7a2b4d6f8091a3c5e7f9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7",
        "sender": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
        "block_number": 61000123,
        "status": "credited",
        "confirmations_required": 19
      },
      "created_at": "2026-09-25T09:58:40.000Z"
    }
  ],
  "next_cursor": null
}

The service fee this account pays

GET /v1/account/fee · getAccountFee

Auth: ApiKey or session.

The rate the next order is charged, the 30-day spend that set it, the next tier and an active promo code. Tiers are by spend over the last 30 days, rolling; a promo code applies when its rate is lower.

Responses

StatusMeaning
200The fee
401Missing, malformed or rejected credentials.
500Something broke on our side.

Response fields (AccountFee)

FieldTypeRequiredDescription
fee_bpsintegeryesThe rate the next order is charged.
sourceenum: tier, promoyes
tier_bpsintegeryesThe rate by volume alone.
spend_30d_suninteger (int64)yesPaid over the last 30 days, rolling, net of refunds, fee included.
next_tierobject (FeeTier) | nullyes
next_tier.from_sunintegeryesThe tier starts at this 30-day spend, SUN, fee included.
next_tier.fee_bpsintegeryes
promoobject | nullyes
promo.codestringyes
promo.fee_bpsintegeryes
promo.expires_atstring (date-time)yes
tiersarray of object (FeeTier)yes
tiers[].from_sunintegeryesThe tier starts at this 30-day spend, SUN, fee included.
tiers[].fee_bpsintegeryes

Apply a promo code

POST /v1/account/promo · redeemPromoCode

Auth: Session.

Sets the account’s service fee to the code’s rate for the code’s term (30 days by default), unless the tier by volume is lower. One code is active at a time: a new code replaces the old one. Entering the active code again changes nothing. Refusals: 3027 promo_not_found (404) and 3028 promo_unavailable (409) with details.reason: inactive, not_started, ended or used_up. Dashboard sign-in only.

Request body

JSON, required.

FieldTypeRequiredDescription
codestringyes

Responses

StatusMeaning
200The fee after the code
400Malformed request — bad JSON, unknown field, wrong type.
401Missing, malformed or rejected credentials.
404No such object, or it belongs to another account. The two are not distinguished.
409The request contradicts the current state of the object.
500Something broke on our side.

Response fields (AccountFee)

FieldTypeRequiredDescription
fee_bpsintegeryesThe rate the next order is charged.
sourceenum: tier, promoyes
tier_bpsintegeryesThe rate by volume alone.
spend_30d_suninteger (int64)yesPaid over the last 30 days, rolling, net of refunds, fee included.
next_tierobject (FeeTier) | nullyes
next_tier.from_sunintegeryesThe tier starts at this 30-day spend, SUN, fee included.
next_tier.fee_bpsintegeryes
promoobject | nullyes
promo.codestringyes
promo.fee_bpsintegeryes
promo.expires_atstring (date-time)yes
tiersarray of object (FeeTier)yes
tiers[].from_sunintegeryesThe tier starts at this 30-day spend, SUN, fee included.
tiers[].fee_bpsintegeryes

The account’s address book

GET /v1/account/addresses · listAddressBook

Auth: Dashboard session cookie + X-CSRF-Token.

Receivers this account saved for reuse, newest first. At most limit (100) entries.

Dashboard session only (member role viewer or above). No API-key scope opens it yet; opening it to balance.read keys is an access decision that has not been taken.

Responses

StatusMeaning
200OK
401Missing, malformed or rejected credentials.
403Authenticated, but this key is not allowed to do it.
500Something broke on our side.

Response fields

FieldTypeRequiredDescription
dataarray of object (AddressBookEntry)yes
data[].idstringyes
data[].labelstringyes
data[].addressstringyesBase58Check TRON address (starts with T, 34 characters).
data[].created_atstring (date-time)yes
data[].updated_atstring (date-time)yes
limitintegeryes
usedintegeryes

Example 200 response, from the contract (illustrative values — live numbers come from the API):

json
{
  "data": [
    {
      "id": "adr_01K5Y9B7C8D9E0F1G2H3J4K5M6",
      "label": "Payouts hot wallet",
      "address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
      "created_at": "2026-09-25T10:20:00.000Z",
      "updated_at": "2026-09-25T10:20:00.000Z"
    }
  ],
  "limit": 100,
  "used": 1
}

Save an address

POST /v1/account/addresses · saveAddressBookEntry

Auth: Dashboard session cookie + X-CSRF-Token.

Adds address under label. The address is unique per account: saving one that is already in the book replaces its label, answers 200 with the existing entry and uses no slot. A new address on a full book is 3019 address_book_full.

Dashboard session, member role editor or above, with X-CSRF-Token.

Request body

JSON (AddressBookRequest), required.

FieldTypeRequiredDescription
labelstringyesTrimmed; 1–64 characters after trimming.
addressstringyesBase58Check TRON address (starts with T, 34 characters).

Example request body, from the contract (illustrative values):

json
{"label":"Payouts hot wallet","address":"TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE"}

Responses

StatusMeaning
200The address was already saved; its label was replaced.
201Saved as a new entry.
400Malformed request — bad JSON, unknown field, wrong type.
401Missing, malformed or rejected credentials.
403Authenticated, but this key is not allowed to do it.
4093019 address_book_full — details.limit, details.used.
500Something broke on our side.

Response fields (AddressBookEntry)

FieldTypeRequiredDescription
idstringyes
labelstringyes
addressstringyesBase58Check TRON address (starts with T, 34 characters).
created_atstring (date-time)yes
updated_atstring (date-time)yes

Remove an address

DELETE /v1/account/addresses/{entryId} · deleteAddressBookEntry

Auth: Dashboard session cookie + X-CSRF-Token.

Frees the slot immediately. An id of another account answers 404, like an id that does not exist. Dashboard session, member role editor, with X-CSRF-Token.

Parameters

NameInTypeRequiredDescription
entryIdpathstringyes

Responses

StatusMeaning
204Removed. No body.
401Missing, malformed or rejected credentials.
403Authenticated, but this key is not allowed to do it.
404No such object, or it belongs to another account. The two are not distinguished.
500Something broke on our side.

Order analytics, aggregated server-side

GET /v1/account/stats · getAccountStats

Auth: API key (HMAC) or dashboard session cookie + X-CSRF-Token.

What the Analytics screen draws, computed next to the data rather than by summing orders in the page. Covers orders created in [from, to):

fieldwhat is counted
ordersevery order, whatever its status
energydelivered energy of energy orders that reached the chain (delegated … reclaimed): delivered_amount, else amount; failed and refunded orders add none
spent_suntotal_amount_sun − refunded_amount_sun of every order, activation included
avg_price_sun_per_65kthe unit price those energy orders were sold at, weighted by delivered energy, × 65,000, rounded to a whole SUN; null without energy

Days and hours are UTC shifted by utc_offset_minutes, so the page can ask for its visitor’s calendar. series has one row per day of the range, zeros included. weekday_hour[d][h] counts orders on ISO weekday d + 1 (Monday first) at hour h.

Retention: 13 months. A from older than that is clamped to the retention start; clamped is true and requested_from echoes what was asked. Defaults: to = now, from = to − 30 days. to must be after from (2001 otherwise).

API key scope orders.read (the aggregates are sums over the orders that scope already lists), or a dashboard session with member role viewer.

Parameters

NameInTypeRequiredDescription
fromquerystring (date-time)
toquerystring (date-time)
bucketqueryenum: day
utc_offset_minutesqueryinteger
group_idquerystringNarrow every figure to orders of this group_id.
labelquerystringNarrow every figure to orders of this label.

Responses

StatusMeaning
200OK
400Malformed request — bad JSON, unknown field, wrong type.
401Missing, malformed or rejected credentials.
403Authenticated, but this key is not allowed to do it.
429Too many requests.
500Something broke on our side.

Response fields (AccountStats)

FieldTypeRequiredDescription
fromstring (date-time)yesThe start actually used
tostring (date-time)yes
requested_fromstring (date-time)yes
clampedbooleanyesTrue when from was older than the retention window and was moved forward.
retention_monthsintegeryes
bucketenum: dayyes
utc_offset_minutesintegeryes
group_idstring | nullyesThe group_id filter in force
labelstring | nullyesThe label filter in force
totalsobjectyes
totals.ordersintegeryes
totals.energyintegeryes
totals.spent_suninteger (int64)yesAn amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
totals.avg_price_sun_per_65kinteger | nullyes
seriesarray of objectyes
series[].datestring (date)yes
series[].ordersintegeryes
series[].energyintegeryes
series[].spent_suninteger (int64)yesAn amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
weekday_hourarray of array of integeryesSeven rows (Monday first) of 24 hourly order counts.
top_receiversarray of objectyes
top_receivers[].receiverstringyesBase58Check TRON address (starts with T, 34 characters).
top_receivers[].ordersintegeryes
top_receivers[].energyintegeryes
top_receivers[].spent_suninteger (int64)yesAn amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
by_grouparray of object (StatsKeyRow)yesPer group_id in the range, most spend first; orders without one are left out.
by_group[].keystringyesThe group id or the label.
by_group[].ordersintegeryes
by_group[].energyintegeryesDelivered energy of energy orders.
by_group[].spent_suninteger (int64)yesAn amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.
by_labelarray of object (StatsKeyRow)yesPer label in the range, most spend first; orders without one are left out.
by_label[].keystringyesThe group id or the label.
by_label[].ordersintegeryes
by_label[].energyintegeryesDelivered energy of energy orders.
by_label[].spent_suninteger (int64)yesAn amount in SUN (1 TRX = 1 000 000 SUN). Always an integer.

Example 200 response, from the contract (illustrative values — live numbers come from the API):

json
{
  "from": "2026-09-23T00:00:00.000Z",
  "to": "2026-09-25T00:00:00.000Z",
  "requested_from": "2026-09-23T00:00:00.000Z",
  "clamped": false,
  "retention_months": 13,
  "bucket": "day",
  "utc_offset_minutes": 0,
  "totals": {"orders":3,"energy":196000,"spent_sun":11760000,"avg_price_sun_per_65k":3900000},
  "series": [
    {"date":"2026-09-23","orders":1,"energy":65000,"spent_sun":3900000},
    {"date":"2026-09-24","orders":2,"energy":131000,"spent_sun":7860000}
  ],
  "weekday_hour": [[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0]],
  "top_receivers": [
    {
      "receiver": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
      "orders": 2,
      "energy": 131000,
      "spent_sun": 7860000
    }
  ]
}

    ↑ ↓ to move · Enter to open · Esc to close