# Subscriptions — API reference

Source: https://tenergy.me/docs/api/subscriptions
Last updated: 2026-09-27

Auto-refill for an address.

| Method | Path | Summary |
|---|---|---|
| `GET` | `/v1/subscriptions/plans` | Subscription presets, limits and the fee rule |
| `GET` | `/v1/subscriptions` | List subscriptions |
| `POST` | `/v1/subscriptions` | Auto-refill an address |
| `GET` | `/v1/subscriptions/{subscriptionId}` | Read a subscription |
| `PATCH` | `/v1/subscriptions/{subscriptionId}` | Change or pause a subscription |
| `DELETE` | `/v1/subscriptions/{subscriptionId}` | Cancel a subscription |

Generated from [`openapi.yaml`](https://tenergy.me/openapi.yaml) at build time. Base URL `https://api.tenergy.me/v1`, or `https://api-nile.tenergy.me/v1` on Nile ([Environments](https://tenergy.me/docs/environments)). Every request below is signed as in [Authentication](https://tenergy.me/docs/authentication) unless its **Auth** line says otherwise.

## Subscription presets, limits and the fee rule

`GET /v1/subscriptions/plans` · `listSubscriptionPlans`

**Auth:** Public — no credentials.

Public; an API key is verified when presented. A preset only fills `reserve`, `low` and
`high`; any rule inside `limits` is accepted (`PlanSubscriptionRequest`). Fee per day =
ceil(reserve × `fee_rule.trx_per_unit` / `fee_rule.unit_energy`) whole TRX. Refills in every
plan are priced at the 1 h grid of the current day-part (`per_use`). `average_price` is
computed server-side for 10/50/200/1,000 uses a day.

### Responses

| Status | Meaning |
|---|---|
| `200` | Active plans, smallest reserve first. |

#### Response fields

| Field | Type | Required | Description |
|---|---|---|---|
| `available` | boolean | yes | False while subscriptions are switched off on this deployment; the plans are shown, not sold. |
| `data` | array of object (SubscriptionPlan) | yes |  |
| `data[].slug` | string | yes |  |
| `data[].name` | string | yes |  |
| `data[].reserve` | integer | yes | Energy kept delegated to the address at most. |
| `data[].low` | integer | yes | Refill when available energy falls below this. |
| `data[].high` | integer | yes | Refill up to this. |
| `data[].fee_sun_per_day` | integer | yes |  |
| `data[].fee_trx_per_day` | number | yes |  |
| `data[].throughput_rule` | string | yes |  |
| `data[].uses_within_reserve_per_day` | integer | yes |  |
| `data[].average_price` | array of object | yes | Average price per use = daily fee / N + per-use price, for N = 10, 50, 200, 1,000. |
| `data[].average_price[].uses_per_day` | integer | yes |  |
| `data[].average_price[].average_sun` | integer \| null | yes |  |
| `data[].average_price[].average_trx` | number \| null | yes |  |
| `data[].average_price[].within_reserve` | boolean | yes |  |
| `per_use` | object | yes |  |
| `per_use.mode` | enum: `grid` | yes | The 1 h energy price of the current day-part × 65,000. |
| `per_use.energy_per_use` | integer | yes |  |
| `per_use.price_sun` | integer \| null | yes | null while 1 h energy is paused. |
| `per_use.price_trx` | number \| null | yes |  |
| `per_use.day_part` | string \| null | yes | Day-part window id. |
| `limits` | object | yes | 131,000 ≤ reserve ≤ 5,240,000; each number a multiple of `step`; low ≥ `low_min`; low < high ≤ reserve; high − low ≥ `gap_min`. Exception: low = high = reserve = 131,000 (Basic). |
| `limits.reserve_min` | integer | yes |  |
| `limits.reserve_max` | integer | yes |  |
| `limits.step` | integer | yes |  |
| `limits.low_min` | integer | yes |  |
| `limits.gap_min` | integer | yes |  |
| `fee_rule` | object | yes |  |
| `fee_rule.trx_per_unit` | integer | yes |  |
| `fee_rule.unit_energy` | integer | yes |  |
| `fee_rule.rounding` | enum: `ceil_whole_trx` | yes |  |
| `at` | string (date-time) | yes |  |

## List subscriptions

`GET /v1/subscriptions` · `listSubscriptions`

**Auth:** API key (HMAC).

Scope `subscriptions.read`, or a dashboard session with member role `viewer`.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `status` | query | enum: `active`, `paused`, `suspended`, `cancelled` |  |  |
| `receiver` | query | string |  |  |
| `limit` | query | integer |  |  |
| `cursor` | query | string |  | Opaque cursor from a previous response's `next_cursor`. |

### Responses

| Status | Meaning |
|---|---|
| `200` | OK |
| `401` | Missing, malformed or rejected credentials. |
| `429` | Too many requests. |
| `500` | Something broke on our side. |

#### Response fields

| Field | Type | Required | Description |
|---|---|---|---|
| `data` | array of object (Subscription) | yes |  |
| `data[].id` | string | yes |  |
| `data[].receiver` | string | yes | Base58Check TRON address (starts with `T`, 34 characters). |
| `data[].resource` | enum: `energy`, `bandwidth` | yes |  |
| `data[].mode` | enum: `refill`, `renewal` | yes |  |
| `data[].tier` | enum: `5m`, `15m`, `1h`, `1d`, `3d`, `30d` | yes | Rental period. The API sells `5m` and `1h`; `1d` exists but is switched off. `15m`, `3d` and `30d` are retired and never sold — they stay in the enum only so old orders parse. Check `available` on each `GET /v1/prices` row; a tier that is not available is rejected with `2003 tier_unavailable`. |
| `data[].threshold_amount` | integer |  |  |
| `data[].refill_amount` | integer |  |  |
| `data[].max_price_sun` | integer (int64) \| null |  |  |
| `data[].max_refills_per_day` | integer \| null |  |  |
| `data[].daily_fee_sun` | integer (int64) |  | Watch fee charged per calendar day while the subscription is not cancelled. |
| `data[].status` | enum: `active`, `paused`, `suspended`, `cancelled` | yes | `paused` is set by you; `suspended` is set by the platform when the balance cannot cover the next refill and clears itself after a deposit; `cancelled` is terminal. |
| `data[].label` | string \| null |  |  |
| `data[].last_refill_at` | string (date-time) \| null |  |  |
| `data[].last_order_id` | string \| null |  |  |
| `data[].refills_today` | integer |  |  |
| `data[].created_at` | string (date-time) | yes |  |
| `data[].updated_at` | string (date-time) |  |  |
| `data[].address` | string |  | Same as `receiver`. Present only on plan subscriptions, with the fields below. |
| `data[].preset` | string \| null |  | Preset slug; null for a custom rule. |
| `data[].reserve` | integer \| null |  | Energy kept delegated at most. |
| `data[].low` | integer \| null |  | Refill when available energy falls below this. |
| `data[].high` | integer \| null |  | Refill up to this. |
| `data[].fee_trx_per_day` | number |  |  |
| `data[].delegated_energy` | integer |  | Energy delegated to the address now. |
| `data[].next_billing_at` | string (date-time) \| null |  |  |
| `data[].grace_until` | string (date-time) \| null |  | Set while a failed daily charge keeps the energy delegated (reported as `suspended`). |
| `data[].events` | array of object (SubscriptionEvent) |  | `GET /v1/subscriptions/{id}` of a plan subscription only: the last 20 events, newest first. |
| `data[].events[].id` | string | yes |  |
| `data[].events[].kind` | enum: `refill`, `charge`, `pause`, `resume`, `cancel`, `top_up_failed` | yes |  |
| `data[].events[].energy_delta` | integer \| null |  |  |
| `data[].events[].amount_sun` | integer \| null |  |  |
| `data[].events[].txid` | string \| null |  |  |
| `data[].events[].ts` | string (date-time) | yes |  |
| `next_cursor` | string \| null | yes |  |

## Auto-refill an address

`POST /v1/subscriptions` · `createSubscription`

**Auth:** API key (HMAC).

Keeps an address supplied with energy. A subscription is three numbers
(`PlanSubscriptionRequest`): `{address, preset}` or `{address, reserve, low, high}`.

* `reserve` (R) — the energy held for the address; 131,000 ≤ R ≤ 5,240,000, step 1,000.
* `low` — refill when the address's free energy drops below it; `low` ≥ 65,000.
* `high` — refill up to it; `high − low` ≥ 65,000 and `high` ≤ R. The `basic` preset
  (`low` = `high` = R) is the one exception.

| Preset | Reserve | Daily fee |
|---|---|---|
| `basic` | 131,000 | 6 TRX |
| `1.3m` | 1,300,000 | 60 TRX |
| `2.62m` | 2,620,000 | 120 TRX |
| `5.24m` | 5,240,000 | 240 TRX |

Billing: the daily fee is `ceil(R × 6 / 131,000)` whole TRX. The first day is charged when
the subscription starts, then every 24 hours. Each refill is charged at the live `1h`
price. A rule outside the limits of `GET /v1/subscriptions/plans` is `422`
`3020 subscription_rule_invalid` with `details.violations`. `PATCH` takes
`{reserve, low, high}`, `{preset}` or `{status}` (`PlanSubscriptionPatch`).

While `GET /v1/subscriptions/plans` → `available` is false this call is `409`
`3021 subscriptions_unavailable`.

One address may have at most one subscription per resource. A second one is rejected with
`3011 subscription_exists`.

Legacy body: `mode: "refill"` / `mode: "renewal"` with `threshold_amount` is still
accepted for existing integrations; new integrations use the three numbers above. The
`201` example shows a legacy subscription; its numbers are illustrative.

Scope `subscriptions.write`, or a dashboard session with member role `editor` and
`X-CSRF-Token`.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `Idempotency-Key` | header | string |  | Client-chosen key making this mutating request safe to retry, 8–128 characters of `A-Z a-z 0-9 . _ : -`. A repeat with the same key and the same body returns the original result; the same key with a different body is rejected with `3010 idempotency_conflict`. Records live for 24 hours. |

### Request body

JSON (`SubscriptionRequest`), required.

| Field | Type | Required | Description |
|---|---|---|---|
| `receiver` | string | yes | Base58Check TRON address (starts with `T`, 34 characters). |
| `resource` | enum: `energy`, `bandwidth` | yes |  |
| `mode` | enum: `refill`, `renewal` | yes | `refill` tops up when the address falls below the threshold; `renewal` keeps a standing rental alive by re-ordering as it expires. |
| `tier` | enum: `5m`, `15m`, `1h`, `1d`, `3d`, `30d` | yes | Rental period. The API sells `5m` and `1h`; `1d` exists but is switched off. `15m`, `3d` and `30d` are retired and never sold — they stay in the enum only so old orders parse. Check `available` on each `GET /v1/prices` row; a tier that is not available is rejected with `2003 tier_unavailable`. |
| `threshold_amount` | integer | yes | Refill when free resource on the address drops below this. |
| `refill_amount` | integer | yes | How much to buy on each refill. |
| `max_price_sun` | integer (int64) |  | Skip a refill whose total would exceed this rather than paying a spike price. A skipped refill produces no order and no webhook; the next check tries again. |
| `max_refills_per_day` | integer |  | Hard stop against a runaway address draining the balance. Once reached, refills pause until the next UTC day. Strongly recommended. |
| `label` | string \| null |  |  |

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

```json
{"address":"TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE","preset":"basic"}
```

### Responses

| Status | Meaning |
|---|---|
| `201` | Created |
| `400` | Malformed request — bad JSON, unknown field, wrong type. |
| `401` | Missing, malformed or rejected credentials. |
| `402` | Not enough balance to cover the order. |
| `409` | The request contradicts the current state of the object. |
| `422` | Syntactically valid but semantically impossible. |
| `429` | Too many requests. |
| `500` | Something broke on our side. |

#### Response fields (`Subscription`)

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes |  |
| `receiver` | string | yes | Base58Check TRON address (starts with `T`, 34 characters). |
| `resource` | enum: `energy`, `bandwidth` | yes |  |
| `mode` | enum: `refill`, `renewal` | yes |  |
| `tier` | enum: `5m`, `15m`, `1h`, `1d`, `3d`, `30d` | yes | Rental period. The API sells `5m` and `1h`; `1d` exists but is switched off. `15m`, `3d` and `30d` are retired and never sold — they stay in the enum only so old orders parse. Check `available` on each `GET /v1/prices` row; a tier that is not available is rejected with `2003 tier_unavailable`. |
| `threshold_amount` | integer |  |  |
| `refill_amount` | integer |  |  |
| `max_price_sun` | integer (int64) \| null |  |  |
| `max_refills_per_day` | integer \| null |  |  |
| `daily_fee_sun` | integer (int64) |  | Watch fee charged per calendar day while the subscription is not cancelled. |
| `status` | enum: `active`, `paused`, `suspended`, `cancelled` | yes | `paused` is set by you; `suspended` is set by the platform when the balance cannot cover the next refill and clears itself after a deposit; `cancelled` is terminal. |
| `label` | string \| null |  |  |
| `last_refill_at` | string (date-time) \| null |  |  |
| `last_order_id` | string \| null |  |  |
| `refills_today` | integer |  |  |
| `created_at` | string (date-time) | yes |  |
| `updated_at` | string (date-time) |  |  |
| `address` | string |  | Same as `receiver`. Present only on plan subscriptions, with the fields below. |
| `preset` | string \| null |  | Preset slug; null for a custom rule. |
| `reserve` | integer \| null |  | Energy kept delegated at most. |
| `low` | integer \| null |  | Refill when available energy falls below this. |
| `high` | integer \| null |  | Refill up to this. |
| `fee_trx_per_day` | number |  |  |
| `delegated_energy` | integer |  | Energy delegated to the address now. |
| `next_billing_at` | string (date-time) \| null |  |  |
| `grace_until` | string (date-time) \| null |  | Set while a failed daily charge keeps the energy delegated (reported as `suspended`). |
| `events` | array of object (SubscriptionEvent) |  | `GET /v1/subscriptions/{id}` of a plan subscription only: the last 20 events, newest first. |
| `events[].id` | string | yes |  |
| `events[].kind` | enum: `refill`, `charge`, `pause`, `resume`, `cancel`, `top_up_failed` | yes |  |
| `events[].energy_delta` | integer \| null |  |  |
| `events[].amount_sun` | integer \| null |  |  |
| `events[].txid` | string \| null |  |  |
| `events[].ts` | string (date-time) | yes |  |

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

```json
{
  "id": "sub_01J9Z7R4Y2AB",
  "receiver": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
  "resource": "energy",
  "mode": "refill",
  "tier": "1h",
  "threshold_amount": 65000,
  "refill_amount": 131000,
  "max_price_sun": 3000000,
  "max_refills_per_day": 48,
  "daily_fee_sun": 3930000,
  "status": "active",
  "last_refill_at": null,
  "last_order_id": null,
  "refills_today": 0,
  "created_at": "2026-09-11T18:30:00.000Z",
  "updated_at": "2026-09-11T18:30:00.000Z"
}
```

## Read a subscription

`GET /v1/subscriptions/{subscriptionId}` · `getSubscription`

**Auth:** API key (HMAC).

Scope `subscriptions.read`, or a dashboard session with member role `viewer`. Another
account's subscription answers `404`.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `subscriptionId` | path | string | yes |  |

### Responses

| Status | Meaning |
|---|---|
| `200` | OK |
| `401` | Missing, malformed or rejected credentials. |
| `404` | No such object, or it belongs to another account. The two are not distinguished. |
| `500` | Something broke on our side. |

#### Response fields (`Subscription`)

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes |  |
| `receiver` | string | yes | Base58Check TRON address (starts with `T`, 34 characters). |
| `resource` | enum: `energy`, `bandwidth` | yes |  |
| `mode` | enum: `refill`, `renewal` | yes |  |
| `tier` | enum: `5m`, `15m`, `1h`, `1d`, `3d`, `30d` | yes | Rental period. The API sells `5m` and `1h`; `1d` exists but is switched off. `15m`, `3d` and `30d` are retired and never sold — they stay in the enum only so old orders parse. Check `available` on each `GET /v1/prices` row; a tier that is not available is rejected with `2003 tier_unavailable`. |
| `threshold_amount` | integer |  |  |
| `refill_amount` | integer |  |  |
| `max_price_sun` | integer (int64) \| null |  |  |
| `max_refills_per_day` | integer \| null |  |  |
| `daily_fee_sun` | integer (int64) |  | Watch fee charged per calendar day while the subscription is not cancelled. |
| `status` | enum: `active`, `paused`, `suspended`, `cancelled` | yes | `paused` is set by you; `suspended` is set by the platform when the balance cannot cover the next refill and clears itself after a deposit; `cancelled` is terminal. |
| `label` | string \| null |  |  |
| `last_refill_at` | string (date-time) \| null |  |  |
| `last_order_id` | string \| null |  |  |
| `refills_today` | integer |  |  |
| `created_at` | string (date-time) | yes |  |
| `updated_at` | string (date-time) |  |  |
| `address` | string |  | Same as `receiver`. Present only on plan subscriptions, with the fields below. |
| `preset` | string \| null |  | Preset slug; null for a custom rule. |
| `reserve` | integer \| null |  | Energy kept delegated at most. |
| `low` | integer \| null |  | Refill when available energy falls below this. |
| `high` | integer \| null |  | Refill up to this. |
| `fee_trx_per_day` | number |  |  |
| `delegated_energy` | integer |  | Energy delegated to the address now. |
| `next_billing_at` | string (date-time) \| null |  |  |
| `grace_until` | string (date-time) \| null |  | Set while a failed daily charge keeps the energy delegated (reported as `suspended`). |
| `events` | array of object (SubscriptionEvent) |  | `GET /v1/subscriptions/{id}` of a plan subscription only: the last 20 events, newest first. |
| `events[].id` | string | yes |  |
| `events[].kind` | enum: `refill`, `charge`, `pause`, `resume`, `cancel`, `top_up_failed` | yes |  |
| `events[].energy_delta` | integer \| null |  |  |
| `events[].amount_sun` | integer \| null |  |  |
| `events[].txid` | string \| null |  |  |
| `events[].ts` | string (date-time) | yes |  |

## Change or pause a subscription

`PATCH /v1/subscriptions/{subscriptionId}` · `updateSubscription`

**Auth:** API key (HMAC).

Send any subset of the mutable fields. `status` accepts `active` and `paused` only —
`suspended` is set by the platform when funds run out and clears itself, and `cancelled`
is reached through `DELETE`. An empty body is rejected with `2002 empty_patch`.

While subscriptions are switched off, a patch that changes the rule, preset or refill
parameters is `409` `3021 subscriptions_unavailable`; `status`, `label`, reads and
`DELETE` keep working so existing subscriptions can be managed.

Scope `subscriptions.write`, or a dashboard session with member role `editor` and
`X-CSRF-Token`.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `subscriptionId` | path | string | yes |  |

### Request body

JSON (`SubscriptionPatch`), required.

| Field | Type | Required | Description |
|---|---|---|---|
| `status` | enum: `active`, `paused` |  |  |
| `tier` | enum: `5m`, `15m`, `1h`, `1d`, `3d`, `30d` |  | Rental period. The API sells `5m` and `1h`; `1d` exists but is switched off. `15m`, `3d` and `30d` are retired and never sold — they stay in the enum only so old orders parse. Check `available` on each `GET /v1/prices` row; a tier that is not available is rejected with `2003 tier_unavailable`. |
| `threshold_amount` | integer |  |  |
| `refill_amount` | integer |  |  |
| `max_price_sun` | integer (int64) |  | An amount in SUN (1 TRX = 1 000 000 SUN). Always an integer. |
| `max_refills_per_day` | integer |  |  |
| `label` | string \| null |  |  |

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

```json
{"status":"paused"}
```

### Responses

| Status | Meaning |
|---|---|
| `200` | Updated |
| `400` | Malformed request — bad JSON, unknown field, wrong type. |
| `401` | Missing, malformed or rejected credentials. |
| `404` | No such object, or it belongs to another account. The two are not distinguished. |
| `409` | The request contradicts the current state of the object. |
| `422` | Syntactically valid but semantically impossible. |
| `500` | Something broke on our side. |

#### Response fields (`Subscription`)

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes |  |
| `receiver` | string | yes | Base58Check TRON address (starts with `T`, 34 characters). |
| `resource` | enum: `energy`, `bandwidth` | yes |  |
| `mode` | enum: `refill`, `renewal` | yes |  |
| `tier` | enum: `5m`, `15m`, `1h`, `1d`, `3d`, `30d` | yes | Rental period. The API sells `5m` and `1h`; `1d` exists but is switched off. `15m`, `3d` and `30d` are retired and never sold — they stay in the enum only so old orders parse. Check `available` on each `GET /v1/prices` row; a tier that is not available is rejected with `2003 tier_unavailable`. |
| `threshold_amount` | integer |  |  |
| `refill_amount` | integer |  |  |
| `max_price_sun` | integer (int64) \| null |  |  |
| `max_refills_per_day` | integer \| null |  |  |
| `daily_fee_sun` | integer (int64) |  | Watch fee charged per calendar day while the subscription is not cancelled. |
| `status` | enum: `active`, `paused`, `suspended`, `cancelled` | yes | `paused` is set by you; `suspended` is set by the platform when the balance cannot cover the next refill and clears itself after a deposit; `cancelled` is terminal. |
| `label` | string \| null |  |  |
| `last_refill_at` | string (date-time) \| null |  |  |
| `last_order_id` | string \| null |  |  |
| `refills_today` | integer |  |  |
| `created_at` | string (date-time) | yes |  |
| `updated_at` | string (date-time) |  |  |
| `address` | string |  | Same as `receiver`. Present only on plan subscriptions, with the fields below. |
| `preset` | string \| null |  | Preset slug; null for a custom rule. |
| `reserve` | integer \| null |  | Energy kept delegated at most. |
| `low` | integer \| null |  | Refill when available energy falls below this. |
| `high` | integer \| null |  | Refill up to this. |
| `fee_trx_per_day` | number |  |  |
| `delegated_energy` | integer |  | Energy delegated to the address now. |
| `next_billing_at` | string (date-time) \| null |  |  |
| `grace_until` | string (date-time) \| null |  | Set while a failed daily charge keeps the energy delegated (reported as `suspended`). |
| `events` | array of object (SubscriptionEvent) |  | `GET /v1/subscriptions/{id}` of a plan subscription only: the last 20 events, newest first. |
| `events[].id` | string | yes |  |
| `events[].kind` | enum: `refill`, `charge`, `pause`, `resume`, `cancel`, `top_up_failed` | yes |  |
| `events[].energy_delta` | integer \| null |  |  |
| `events[].amount_sun` | integer \| null |  |  |
| `events[].txid` | string \| null |  |  |
| `events[].ts` | string (date-time) | yes |  |

## Cancel a subscription

`DELETE /v1/subscriptions/{subscriptionId}` · `cancelSubscription`

**Auth:** API key (HMAC).

Stops future refills. Rentals already delivered keep running until they expire; they are
not reclaimed and not refunded. The subscription stays readable with `status: "cancelled"`.
Scope `subscriptions.write`, or a dashboard session with member role `editor` and
`X-CSRF-Token`.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `subscriptionId` | path | string | yes |  |

### Responses

| Status | Meaning |
|---|---|
| `204` | Cancelled. No body. |
| `401` | Missing, malformed or rejected credentials. |
| `404` | No such object, or it belongs to another account. The two are not distinguished. |
| `500` | Something broke on our side. |
