GET /ext/packages

/ext/packages returns every package the calling key can sell. Packages are the price sheet of the panel: each row describes a subscription (credit cost, duration, max connections) and, optionally, its trial variant. Storefronts, dashboards, and any flow that creates a line off a package name reads this list.

The response shape is the OneStream shape: a plain JSON array of package objects. There is no envelope. If your integration handles the native v1 API too, note that this endpoint corresponds to GET /panel-api/v1/packages, but the v1 flavor wraps the same rows in {"items": [...]}. Here you get the rows directly.

Endpoint

GET https://<your-panel-domain>/panel-api/onestream/ext/packages

Authentication

Send the API key in X-Api-Key, X-Auth-User, or Authorization: Bearer. See the OneStream overview.

Required scope

packages:read.

Query parameters

None. The full list of visible packages is returned in one call. There are typically fewer than a few hundred packages per panel, so pagination is not applied. The list is scoped silently by caller type: an admin key sees every package, a reseller key sees only packages whose groups list contains the reseller's member_group_id. No error is raised when the visible set is empty; the response is [].

Response

A plain JSON array. Each element has the following fields.

Field Type Description
id int Numeric package id. Stable across the panel's lifetime. Pass this as package when creating a line.
package_name string Human-readable name shown in the panel UI.
is_trial bool true when the package can only be sold as a trial. Paid fields will be zero.
is_official bool false marks the package as internal or hidden from public storefronts.
official_credits float Credits charged when the package is sold as a paid subscription.
official_duration int Length of the paid subscription in official_duration_in units.
official_duration_in string One of hours, days, weeks, months, years.
trial_credits float Credits charged when the caller requests a trial. 0 if trials are disabled.
trial_duration int Length of the trial in trial_duration_in units. 0 if trials are disabled.
trial_duration_in string Same allowed values as official_duration_in.
max_connections int Maximum concurrent devices a subscriber on this package can have logged in.
is_restreamer bool true when the package enables restreamer permissions.
forced_country string Two-letter ISO country code that forces every line created under this package to that country. Empty string means no restriction.

Example response:

[
  {
    "id": 76,
    "package_name": "Package 12M 1 Connection",
    "is_trial": false,
    "is_official": true,
    "official_credits": 12,
    "official_duration": 12,
    "official_duration_in": "months",
    "trial_credits": 0,
    "trial_duration": 0,
    "trial_duration_in": "hours",
    "max_connections": 1,
    "is_restreamer": false,
    "forced_country": ""
  },
  {
    "id": 67,
    "package_name": "Package 1M 1 Connection",
    "is_trial": false,
    "is_official": true,
    "official_credits": 1,
    "official_duration": 1,
    "official_duration_in": "months",
    "trial_credits": 0,
    "trial_duration": 0,
    "trial_duration_in": "hours",
    "max_connections": 1,
    "is_restreamer": false,
    "forced_country": ""
  }
]

Examples

cURL

curl -H "X-Api-Key: <your-api-key>" \
     https://<your-panel-domain>/panel-api/onestream/ext/packages

PHP raw

$ch = curl_init('https://<your-panel-domain>/panel-api/onestream/ext/packages');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Api-Key: <your-api-key>']);
$packages = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($packages as $p) {
    echo $p['id'] . ' ' . $p['package_name'] . ' (' . $p['official_credits'] . " credits)\n";
}

Python raw

import requests

packages = requests.get(
    "https://<your-panel-domain>/panel-api/onestream/ext/packages",
    headers={"X-Api-Key": "<your-api-key>"},
    timeout=30,
).json()

for p in packages:
    print(p["id"], p["package_name"], p["official_credits"])

Errors

HTTP Error slug When it happens How to fix
401 invalid_key Header is missing, the token is unknown, the key was disabled, expired, deleted, or the caller IP is not on the key's IP allow-list. Check the header. If the key was rotated, mint a new one from the panel.
403 insufficient_scope The key does not carry packages:read. Regenerate the key with packages:read, or use a key that has it.
429 rate_limited The per-key or per-IP rate limit was hit. Response carries Retry-After and X-RateLimit-* headers. Back off for the number of seconds in Retry-After. Cache the response.
403 api_disabled An admin has turned the Panel API off for this panel. Ask the admin to re-enable it.

See also