---
title: "Update a line"
description: "Partially update an existing line. Admin-only endpoint that patches any subset of operator-level fields."
---

# Update a line

Patch operator-level fields on an existing line. This endpoint is a partial update: only the fields you send are written, everything else stays as it was.

The endpoint is **admin only**. Reseller keys receive `403 admin_only_endpoint`. This is the only endpoint that can flip `admin_enabled`, set `allowed_ips` / `allowed_ua`, or move `exp_date` freely (including making a line perpetual by sending `exp_date: null`).

## Endpoint

`POST https://<your-panel-domain>/panel-api/v1/lines/{id}/update`

## Authentication

Bearer token in the `Authorization` header. The token must belong to an admin key. See [Panel API authentication](/docs/?page=panel-api-authentication).

## Required scope

`lines:write`. The endpoint additionally rejects reseller keys, regardless of scope.

## Idempotency

Required. Every write must send an `Idempotency-Key` header. Retrying the same key with the same body returns the original response; a different body returns `409 idempotency_conflict`; a missing header returns `400 missing_idempotency_key`.

## Path parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `id` | int | The line id to update. |

## Request body

Every field is optional; only the fields present in the body are written.

| Field | Type | Required | Default | Description |
| ----- | ---- | -------- | ------- | ----------- |
| `password` | string | no | | Set a new password. To generate a random one instead, use [reset-password](/docs/?page=xai-ref-lines-reset-password). |
| `exp_date` | int or `null` | no | | UTC epoch. Sending `null` explicitly makes the line perpetual. Omitting the field leaves the current expiry alone. Not range-validated here (unlike create). |
| `max_connections` | int | no | | Clamped to `[1, 100]`. |
| `is_restreamer` | bool | no | | Flip the restreamer flag. |
| `enabled` | bool | no | | Reseller-visible toggle. |
| `admin_enabled` | bool | no | | Hard admin override. If false, the line is blocked no matter what `enabled` says. |
| `allowed_ips` | string[] | no | | IPv4 allow-list, up to 50 entries. Invalid entries drop silently. |
| `allowed_ua` | string[] | no | | User-Agent allow-list, up to 50 entries, each capped at 500 chars. |

```json
{
  "max_connections": 4,
  "is_restreamer": true,
  "allowed_ips": ["203.0.113.7", "198.51.100.42"],
  "allowed_ua": ["ExampleTV/1.0"]
}
```

## Response

`200 OK` with the full line object reflecting the new field values.

```json
{
  "id": 172504291,
  "username": "u_a1b2c3d4",
  "password": "8ebc79cf",
  "member_id": 100,
  "exp_date": 1788885052,
  "max_connections": 4,
  "is_trial": false,
  "is_restreamer": true,
  "enabled": true,
  "admin_enabled": true,
  "bouquets": [],
  "created_at": 1786206652
}
```

## Examples

### cURL

```bash
curl -X POST https://<your-panel-domain>/panel-api/v1/lines/172504291/update \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: line-172504291-bump-caps-2026-08-08" \
  -d '{
    "max_connections": 4,
    "is_restreamer": true,
    "allowed_ips": ["203.0.113.7", "198.51.100.42"],
    "allowed_ua": ["ExampleTV/1.0"]
  }'
```

### PHP SDK

```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>');
$line = $client->lines->update(
    id:             172504291,
    maxConnections: 4,
    isRestreamer:   true,
    allowedIps:     ['203.0.113.7', '198.51.100.42'],
    allowedUa:      ['ExampleTV/1.0'],
    idempotencyKey: 'line-172504291-bump-caps-2026-08-08',
);
echo $line->maxConnections, PHP_EOL;
```

### Python SDK

```python
from xtream_ai_panel_api import PanelApiClient

client = PanelApiClient(base_url="https://<your-panel-domain>", token="<your-api-key>")
line = client.lines.update(
    id=172504291,
    max_connections=4,
    is_restreamer=True,
    allowed_ips=["203.0.113.7", "198.51.100.42"],
    allowed_ua=["ExampleTV/1.0"],
    idempotency_key="line-172504291-bump-caps-2026-08-08",
)
print(line.max_connections)
```

> [!NOTE]
> `exp_date` is three-state: omit the argument to leave the current expiry untouched, pass a UTC epoch to set a new one, or pass `null` / `None` to make the line perpetual (`$client->lines->update(id: 172504291, expDate: null)` / `client.lines.update(id=172504291, exp_date=None)`).

## Errors

| HTTP | Error slug | When it happens | How to fix |
| ---- | ---------- | --------------- | ---------- |
| 400 | `missing_idempotency_key` | The `Idempotency-Key` header was not sent. | Send a per-intent key on every write. |
| 401 | `invalid_key` | Token is unknown, expired, disabled, or deleted. | Check the token or issue a new one. |
| 403 | `admin_only_endpoint` | A reseller key called this endpoint. | Call from an admin key. |
| 403 | `insufficient_scope` | Token lacks `lines:write`. | Issue a key with the scope. |
| 404 | `not_found` | The line id does not exist. | Verify the id (`GET /lines/{id}`). |
| 409 | `idempotency_conflict` | Same key was reused with a different body. | Use a fresh key. |
| 409 | `idempotency_in_flight` | Same key is still processing. | Retry after a moment. |
| 429 | `rate_limited` | The key hit its per-minute cap. | Back off and retry after `Retry-After` seconds. |

## See also

- [Create a line](/docs/?page=xai-ref-lines-create)
- [Enable a line](/docs/?page=xai-ref-lines-enable)
- [Disable a line](/docs/?page=xai-ref-lines-disable)
- [Panel API lines overview](/docs/?page=xai-ref-lines-list)
