Disable a line

If you are starting a new integration instead of migrating an existing one, prefer the native v1 API with the official SDKs. The XC dialect keeps compatibility with legacy tooling; the native dialect gives you typed models, header-based idempotency, and structured HTTP status codes.

action=disable_line flips the reseller-visible enabled flag on a subscriber line to false. The request is translated to the native POST /panel-api/v1/lines/{id}/disable and rewrapped in the classic {"status": "STATUS_SUCCESS", "data": {...}} envelope. The change is instantaneous; active connections keep playing until the client reconnects, then the panel refuses them.

Two flags govern whether a line serves traffic:

  • enabled is the reseller-visible toggle. enable_line and disable_line move it.
  • admin_enabled is the admin override. If it is false, the line stays blocked no matter what enabled says. Only edit_line on an admin key can change it.

Disable does not:

  • Charge credits. It is always free, in every billing mode, on both admin and reseller keys.
  • Refund credits already spent. Credits paid to create or extend the line stay spent.
  • Delete the line. The row and its bouquets stay in the database. Re-enable with enable_line or delete definitively with delete_line.
  • Free the slot in users billing mode. The slot count is based on line existence, not on the enabled flag. To free a slot, delete_line is required.

Endpoint

POST https://<your-panel-domain>/panel-api/xc/{accesscode}/admin/index.php?action=disable_line

Both /admin/index.php and /reseller/index.php are accepted. The admin-versus-reseller decision comes from the key.

Authentication

Any one of these three forms:

  • ?api_key=<your-api-key> in the query string.
  • api_key=<your-api-key> in the POST body form field.
  • Authorization: Bearer <your-api-key> HTTP header.

See Authentication.

Required scope

lines:write.

Idempotency

Optional. Pass rid=<unique-per-operation> to opt in. Disable is naturally idempotent (twice-disabled stays disabled), so the practical benefit is limited to replaying the response payload. See the idempotency section.

Request body

Field Type Required Default Description
id int yes The line to disable.
rid string no Idempotency identifier.

Response

data is the full Line object with enabled=false. Same shape used by action=get_line.

{
  "status": "STATUS_SUCCESS",
  "data": {
    "id": 172511994,
    "username": "u_a1b2c3d4",
    "password": "Sup3rSecret1",
    "member_id": 100,
    "exp_date": 1849365538,
    "max_connections": 3,
    "is_trial": false,
    "is_restreamer": false,
    "enabled": false,
    "admin_enabled": true,
    "bouquets": [2, 4],
    "created_at": 1786207138
  }
}

HTTP status is always 200, even on failure.

Examples

cURL

curl -X POST "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php?api_key=<your-api-key>&action=disable_line" \
  -d "id=172511994" \
  -d "rid=dis-172511994-2026-01-15"

PHP (raw HTTP)

$url = 'https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php'
     . '?' . http_build_query(['api_key' => '<your-api-key>', 'action' => 'disable_line']);
$body = http_build_query([
    'id'  => 172511994,
    'rid' => 'dis-172511994-' . bin2hex(random_bytes(8)),
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $body,
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);
if (($resp['status'] ?? '') !== 'STATUS_SUCCESS') {
    throw new RuntimeException($resp['data']['message'] ?? 'disable_line failed');
}

Python (raw HTTP)

import requests, secrets

r = requests.post(
    "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php",
    params={"api_key": "<your-api-key>", "action": "disable_line"},
    data={
        "id":  172511994,
        "rid": f"dis-172511994-{secrets.token_hex(8)}",
    },
    timeout=30,
)
r.raise_for_status()
body = r.json()
if body.get("status") != "STATUS_SUCCESS":
    raise RuntimeError(body["data"].get("message", "disable_line failed"))

Errors

Response is always HTTP 200. Branch on status, then data.error.

status Error slug When it happens How to fix
STATUS_INVALID_DATA validation_error id is missing or empty. data.details.field is "id". Send a numeric id.
STATUS_FAILURE not_found The id does not exist, or a reseller key targeted a line owned by another reseller. Verify the id and ownership.
STATUS_NO_PERMISSIONS insufficient_scope The key does not have lines:write. Grant the scope.
STATUS_FAILURE idempotency_conflict Same rid reused with a different body. Pick a new rid.
STATUS_FAILURE idempotency_in_flight Same rid is still processing. Retry after a moment.
STATUS_FAILURE invalid_key Token missing, unknown, disabled, expired, or IP not in allow-list. Verify the token and the IP allow-list.
STATUS_FAILURE rate_limited Per-minute cap or per-IP cap exceeded. Back off.
STATUS_FAILURE api_disabled Panel API is switched off. Contact the panel admin.

See also