Docs menu

API keys — API reference

Managing the credentials of this account.

MethodPathSummary
GET/v1/api-keysList the keys of this account
POST/v1/api-keysCreate an API key
PATCH/v1/api-keys/{keyId}Edit a key
DELETE/v1/api-keys/{keyId}Revoke a key

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.

List the keys of this account

GET /v1/api-keys · listApiKeys

Auth: API key (HMAC).

Secrets are never returned by this endpoint — a secret exists in a response exactly once, at creation. The key shown here is the public identifier sent in X-API-KEY.

Responses

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

Response fields

FieldTypeRequiredDescription
dataarray of object (ApiKey)yes
data[].idstringyes
data[].keystringyesThe public identifier sent in X-API-KEY. Not secret.
data[].labelstringyes
data[].scopesarray of enum (13 values, ApiKeyScope)yes
data[].ip_allowlistarray of string
data[].compat_dialectstring | nullThe compatible-API dialect of this key, null for a native key.
data[].daily_limit_suninteger (int64) | nullThe most orders placed with this key may cost per UTC day, in SUN; null = no limit.
data[].monthly_limit_suninteger (int64) | nullThe same per UTC calendar month; null = no limit.
data[].created_by_agentobject | nullThe AI agent (connected-agent grant) that created this key; null for a key a person or another key created. Its spend limits stay within that agent’s unless the account owner changes them in the dashboard.
data[].created_by_agent.grant_idstring | nullyesThe grant; null once that grant was deleted.
data[].created_by_agent.client_namestringyesThe agent’s name when it created the key.
data[].is_activebooleanyes
data[].last_used_atstring (date-time) | nullWhen this key last signed a request. Written at most once a minute per key, so it answers “is this key still in use”, not “to the second, when”.
data[].last_used_ipstring | nullThe source address of that last request. null until the key is used.
data[].expires_atstring (date-time) | null
data[].created_atstring (date-time)yes
limitintegeryesHow many active keys this account may hold at once. A product limit, not a permission: it caps how many credentials exist, never what any of them may do. Past it, POST /api-keys answers 3017 api_key_limit_reached; revoking a key frees a slot immediately.
usedintegeryesActive keys right now — revoked and expired ones do not count.

Create an API key

POST /v1/api-keys · createApiKey

Auth: API key (HMAC) or bootstrap token.

Creates a key and returns its secret once. The secret is not stored in a recoverable form; if it is lost, delete the key and create another.

The first key of an account is created with the bootstrap token from the signup flow; every later one with an existing key that itself holds keys.create.

Also on an unfunded account (since 2026-09-26), so an integration can read its deposit address with the new key and top up without a human in the loop.

scopes is required and has no default. The API deliberately does not invent a starter set of permissions: a key’s reach is a decision for the account owner, taken with the list in front of them. A request without scopes is rejected with 2001 validation_failed, and no client library, agent flow or dashboard form may supply one on the owner’s behalf.

One permission vocabulary

Permissions are named area.action and there is one list of names for the whole platform. The names below are the ones an API key may carry in v1. The remaining names in that vocabulary — balance.withdraw, billing.write, invoices.read, referral.read, referral.withdraw, team.read, team.invite, team.remove, team.grant, settings.write, audit.read — exist for use elsewhere in the platform (team management in the dashboard) but are not key-eligible in v1.

ScopeWhat it opens
prices.readPOST /estimate/transfer (GET /prices, GET /estimate and GET /resources/{address} are public and need no scope)
balance.readGET /account, GET /balance — balances and lifetime counters
balance.topup_addressGET /deposit-addresses — the account’s deposit address and its retired ones
orders.readGET /orders (also as CSV), GET /orders/{id}, GET /batches…, GET /quotes/{id}, GET /account/stats (sums over the same orders)
orders.createPOST /orders, POST /quotes, POST /batches, POST /batches/{id}/cancel — spends money
orders.reclaimPOST /orders/{id}/reclaim — ends a rental early, no refund
subscriptions.readGET /subscriptions…
subscriptions.writecreate, patch, cancel subscriptions — commits to recurring spend
webhooks.readGET /webhooks…, including GET /webhooks/{id}/deliveries
webhooks.writecreate, patch, delete, rotate, test webhook endpoints
keys.readGET /api-keys
keys.createPOST /api-keys, PATCH /api-keys/{id} — a key with this can widen its own account’s reach
keys.revokeDELETE /api-keys/{id} — can stop a production integration instantly

GET /ledger, GET /account/addresses and GET /session/history are dashboard-session operations and no scope opens them to a key.

A key can only be created with scopes that the creating key itself holds, so keys.create cannot be used to escalate beyond what the caller already has. A key created with the bootstrap token may carry any key-eligible scope, because the signing address is the account’s owner.

Keys created by an AI agent (OAuth access token with api_keys.manage): a key that can spend (orders.create, subscriptions.write) gets the connected agent’s own daily and monthly limits when daily_limit_trx / monthly_limit_trx are omitted. A limit above the agent’s, or null while the agent has one, is refused with 403 3032 agent_key_limit_exceeded (details.grant_daily_limit_sun, details.grant_monthly_limit_sun). Only the account owner sets other limits, in the dashboard. Such a key shows created_by_agent.

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 (ApiKeyRequest), required.

FieldTypeRequiredDescription
labelstringOptional. Omitted, the key is named Key N at the next free index, so several keys can be created without inventing names for them. A label is a name, not a permission — which is why it may have a default and scopes never will.
scopesarray of enum (13 values, ApiKeyScope)yesRequired, no default, no server-side suggestion. The names are the key-eligible subset of the one platform permission vocabulary; see the endpoint description for what each opens.
ip_allowlistarray of stringSource addresses allowed to use this key, IPv4/IPv6 addresses or CIDR blocks. An empty or absent list means any IP — acceptable for a read-only key, a bad idea for one that can spend.
expires_atstring (date-time) | nullAutomatic revocation time. null for a key that does not expire.
compat_dialectstringMakes this a key for a compatible API: it answers another service’s API under /compat/<dialect>/… with that service’s paths, fields, errors and auth scheme, so an integration written for it moves here by changing the base URL and the key. Plain energy rental only (price, order, status, balance, activation); a 1-hour term. Such a key may hold only orders.create, orders.read, balance.read, balance.topup_address, prices.read, and orders.reclaim for the services with an early return: iTRX, trxx.io, CatFee, Netts, SoHu, Tron Energy Market; a Netts key may also hold webhooks.read / webhooks.write and then sees and changes only the endpoints it registered. Values: the dialect ids listed in the compatibility docs.
daily_limit_trxnumber | nullThe most orders placed with this key may cost per UTC day, in TRX. Past it an order is refused with 3026 api_key_spend_limit_reached until 00:00 UTC. Absent or null = no daily limit. Compatible-API calls count too. For a spending key created by an AI agent: absent = the agent’s own daily limit, and null or more than the agent’s limit is refused with 3032 agent_key_limit_exceeded.
monthly_limit_trxnumber | nullThe same per UTC calendar month. Absent or null = no monthly limit (agent keys as above).

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

json
{
  "label": "grafana exporter",
  "scopes": ["balance.read","orders.read"],
  "ip_allowlist": ["203.0.113.10"]
}

Responses

StatusMeaning
201Created. secret appears only here.
400Malformed request — bad JSON, unknown field, wrong type.
401Missing, malformed or rejected credentials.
403Authenticated, but this key is not allowed to do it.
4093017 api_key_limit_reached — the account already holds the maximum number of active keys. details.limit and details.used; revoke one to free a slot.
422Syntactically valid but semantically impossible.
500Something broke on our side.

Response fields

FieldTypeRequiredDescription
idstringyes
keystringyesThe public identifier sent in X-API-KEY. Not secret.
labelstringyes
scopesarray of enum (13 values, ApiKeyScope)yes
ip_allowlistarray of string
compat_dialectstring | nullThe compatible-API dialect of this key, null for a native key.
daily_limit_suninteger (int64) | nullThe most orders placed with this key may cost per UTC day, in SUN; null = no limit.
monthly_limit_suninteger (int64) | nullThe same per UTC calendar month; null = no limit.
created_by_agentobject | nullThe AI agent (connected-agent grant) that created this key; null for a key a person or another key created. Its spend limits stay within that agent’s unless the account owner changes them in the dashboard.
created_by_agent.grant_idstring | nullyesThe grant; null once that grant was deleted.
created_by_agent.client_namestringyesThe agent’s name when it created the key.
is_activebooleanyes
last_used_atstring (date-time) | nullWhen this key last signed a request. Written at most once a minute per key, so it answers “is this key still in use”, not “to the second, when”.
last_used_ipstring | nullThe source address of that last request. null until the key is used.
expires_atstring (date-time) | null
created_atstring (date-time)yes
secretstringyesHMAC secret, 256 bits, shown once. sk_live_… on the production host and sk_test_… on the Nile host, matching the key id. It is not stored in a recoverable form: if it is lost, revoke the key and create another.

Edit a key

PATCH /v1/api-keys/{keyId} · updateApiKey

Auth: API key (HMAC).

Change label, ip_allowlist, is_active, or scopes. Narrowing scopes takes effect immediately. Widening them is subject to the same rule as creation: the calling key must already hold every scope being granted.

An AI agent may only lower a key’s spend limits or set them within its own limits; it cannot remove one while it has a limit itself, nor add a spending scope to a key whose limits exceed its own. Otherwise 403 3032 agent_key_limit_exceeded.

Parameters

NameInTypeRequiredDescription
keyIdpathstringyes

Request body

JSON (ApiKeyPatch), required.

FieldTypeRequiredDescription
labelstring
scopesarray of enum (13 values, ApiKeyScope)
ip_allowlistarray of string
is_activeboolean
expires_atstring (date-time) | null
daily_limit_trxnumber | nullA new daily spend limit in TRX; null removes it.
monthly_limit_trxnumber | nullA new monthly spend limit in TRX; null removes 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.
404No such object, or it belongs to another account. The two are not distinguished.
422Syntactically valid but semantically impossible.
500Something broke on our side.

Response fields (ApiKey)

FieldTypeRequiredDescription
idstringyes
keystringyesThe public identifier sent in X-API-KEY. Not secret.
labelstringyes
scopesarray of enum (13 values, ApiKeyScope)yes
ip_allowlistarray of string
compat_dialectstring | nullThe compatible-API dialect of this key, null for a native key.
daily_limit_suninteger (int64) | nullThe most orders placed with this key may cost per UTC day, in SUN; null = no limit.
monthly_limit_suninteger (int64) | nullThe same per UTC calendar month; null = no limit.
created_by_agentobject | nullThe AI agent (connected-agent grant) that created this key; null for a key a person or another key created. Its spend limits stay within that agent’s unless the account owner changes them in the dashboard.
created_by_agent.grant_idstring | nullyesThe grant; null once that grant was deleted.
created_by_agent.client_namestringyesThe agent’s name when it created the key.
is_activebooleanyes
last_used_atstring (date-time) | nullWhen this key last signed a request. Written at most once a minute per key, so it answers “is this key still in use”, not “to the second, when”.
last_used_ipstring | nullThe source address of that last request. null until the key is used.
expires_atstring (date-time) | null
created_atstring (date-time)yes

Revoke a key

DELETE /v1/api-keys/{keyId} · deleteApiKey

Auth: API key (HMAC).

Immediate and irreversible. In-flight requests signed with this key start failing at once; orders it already created are unaffected and keep running.

A key cannot delete itself — that would leave an account with no way back in if it were the last one. Revoking your own key is done from the dashboard.

Parameters

NameInTypeRequiredDescription
keyIdpathstringyes

Responses

StatusMeaning
204Revoked. 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.

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