SDK Cheat Sheet
Every operation the SDK supports, one line of PHP + one line of Python. For the full documentation of each call (parameters, defaults, response fields, error slugs), follow the link on the right. Assumes a client already constructed as:
<?php
require __DIR__ . '/api-panel-php-sdk-1.0.0/autoload.php';
use XtreamAI\PanelApi\PanelApiClient;
$client = new PanelApiClient(
baseUrl: 'https://<your-panel-domain>',
token: '<your-api-key>',
);
from xtream_ai_panel_api import PanelApiClient
client = PanelApiClient(
base_url="https://<your-panel-domain>",
token="<your-api-key>",
)
Identity
| PHP | Python | What it returns |
|---|---|---|
$client->me->get() |
client.me.get() |
Caller identity (type, scopes, billing snapshot). See Get caller identity. |
$client->health() |
client.health() |
Public liveness probe. No auth required. See Health check. |
Lines
| PHP | Python | Docs |
|---|---|---|
$client->lines->list(limit: 50) |
client.lines.list(limit=50) |
List lines |
$client->lines->get(172504295) |
client.lines.get(172504295) |
Retrieve line |
$client->lines->create(packageId: 42, memberId: 100) |
client.lines.create(package_id=42, member_id=100) |
Create line |
$client->lines->update(172504295, notes: 'renewed') |
client.lines.update(172504295, notes="renewed") |
Update line |
$client->lines->enable(172504295) |
client.lines.enable(172504295) |
Enable line |
$client->lines->disable(172504295) |
client.lines.disable(172504295) |
Disable line |
$client->lines->renew(172504295, packageId: 42) |
client.lines.renew(172504295, package_id=42) |
Renew line |
$client->lines->resetPassword(172504295) |
client.lines.reset_password(172504295) |
Reset password |
$client->lines->delete(172504295) |
client.lines.delete(172504295) |
Delete line |
$client->lines->connections(172504295) |
client.lines.connections(172504295) |
List connections |
Catalog
| PHP | Python | Docs |
|---|---|---|
$client->catalog->packages() |
client.catalog.packages() |
List packages |
$client->catalog->bouquets() |
client.catalog.bouquets() |
List bouquets |
$client->catalog->streams(limit: 100) |
client.catalog.streams(limit=100) |
List live streams |
$client->catalog->stream(482) |
client.catalog.stream(482) |
Retrieve stream |
$client->catalog->vods(search: 'inception') |
client.catalog.vods(search="inception") |
List VODs |
$client->catalog->vod(9042) |
client.catalog.vod(9042) |
Retrieve VOD |
Resellers
Admin-only for most operations. A reseller with subresellers:write can create sub-resellers under itself.
| PHP | Python | Docs |
|---|---|---|
$client->resellers->list() |
client.resellers.list() |
List resellers |
$client->resellers->get(260595) |
client.resellers.get(260595) |
Retrieve reseller |
$client->resellers->create(username: 'newco', memberGroupId: 3) |
client.resellers.create(username="newco", member_group_id=3) |
Create reseller |
$client->resellers->update(260595, notes: '...') |
client.resellers.update(260595, notes="...") |
Update reseller |
$client->resellers->billing(260595) |
client.resellers.billing(260595) |
Read billing |
$client->resellers->adjustBilling(260595, delta: 100) |
client.resellers.adjust_billing(260595, delta=100) |
Adjust billing |
Pagination
Every list() returns a PaginatedResponse with items and an opaque nextCursor (next_cursor). The SDK does not loop for you — pass the cursor back to fetch the next page.
<?php
$page = $client->lines->list(limit: 50);
while (true) {
foreach ($page->items as $line) { /* ... */ }
if ($page->nextCursor === null) break;
$page = $client->lines->list(limit: 50, cursor: $page->nextCursor);
}
page = client.lines.list(limit=50)
while True:
for line in page.items:
pass # ...
if page.next_cursor is None:
break
page = client.lines.list(limit=50, cursor=page.next_cursor)
Full walk-through in SDKs Overview → Pagination.
Idempotency
Every write auto-generates a UUID. Override with a stable business key when the caller is retriable (webhooks, job queues, cron):
<?php
$client->lines->create(
packageId: 42, memberId: 100, username: 'johndoe',
idempotencyKey: 'invoice-INV-42',
);
client.lines.create(
package_id=42, member_id=100, username="johndoe",
idempotency_key="invoice-INV-42",
)
Full contract in Retry and Idempotency with the SDK.
Error handling
Catch specific first, base class last. Every exception has .slug, .request_id, .status_code, .details.
<?php
use XtreamAI\PanelApi\Exceptions\{
InsufficientCreditsException, ValidationException, PanelApiException,
};
try {
$client->lines->create(packageId: 42, memberId: 100);
} catch (InsufficientCreditsException $e) {
// Reseller is broke.
} catch (ValidationException $e) {
error_log("bad field: " . $e->field());
} catch (PanelApiException $e) {
error_log("api error req={$e->requestId} slug={$e->slug}");
}
from xtream_ai_panel_api.exceptions import (
InsufficientCreditsException, ValidationException, PanelApiException,
)
try:
client.lines.create(package_id=42, member_id=100)
except InsufficientCreditsException:
pass # Reseller is broke.
except ValidationException as e:
print("bad field:", e.field)
except PanelApiException as e:
print(f"api error req={e.request_id} slug={e.slug}")
Full exception hierarchy in Errors with the SDK.
Common one-shot flows
Provision a new customer after payment
<?php
$line = $client->lines->create(
packageId: 42,
memberId: 100,
email: 'johndoe@example.com',
idempotencyKey: 'payment-'.$paymentId,
);
echo "Deliver credentials: {$line->username} / {$line->password}", PHP_EOL;
line = client.lines.create(
package_id=42, member_id=100, email="johndoe@example.com",
idempotency_key=f"payment-{payment_id}",
)
print(f"Deliver credentials: {line.username} / {line.password}")
Renew a subscription
<?php
$line = $client->lines->renew(
$lineId,
packageId: 42,
idempotencyKey: 'renewal-'.$invoiceId,
);
echo "New expiry: {$line->expDate}", PHP_EOL;
line = client.lines.renew(
line_id, package_id=42,
idempotency_key=f"renewal-{invoice_id}",
)
print(f"New expiry: {line.exp_date}")
Suspend a delinquent account
<?php
$client->lines->disable($lineId, idempotencyKey: 'suspend-'.$lineId);
client.lines.disable(line_id, idempotency_key=f"suspend-{line_id}")
Rotate a compromised customer password
<?php
$newPassword = $client->lines->resetPassword($lineId);
notifyCustomer($newPassword);
new_password = client.lines.reset_password(line_id)
notify_customer(new_password)
Reconcile: is this line still active on the panel?
<?php
try {
$line = $client->lines->get($lineId);
$active = $line->enabled && !$line->isExpired;
} catch (\XtreamAI\PanelApi\Exceptions\NotFoundException) {
$active = false;
}
from xtream_ai_panel_api.exceptions import NotFoundException
try:
line = client.lines.get(line_id)
active = line.enabled and not line.is_expired
except NotFoundException:
active = False
Constants worth memorizing
- Default timeout: 30 s per request.
- Default retries: 3 attempts on top of the original.
- Max
limiton any list endpoint: 100 (some default 50). - Idempotency window on the server: at least 24 hours.
- 429 Retry-After cap honored by the SDK: 60 s.
See also
- SDKs Overview. The full picture: what you get vs raw HTTP.
- Common Tasks. Longer worked recipes with the full context.
- Working with Lines · Working with Catalog · Working with Resellers. Resource-level guides.