Update a line, POST /ext/line/{uuid}/update-advanced

If you are starting a new integration instead of migrating, prefer the native v1 API with the official SDKs. The OneStream dialect exists to let existing OneStream code point at Xtream AI with only a base URL change.

Change selected fields on an existing line. Seven fields are editable through this endpoint: password, expire_at, is_enabled, is_restreamer, max_connections, bouquets, and reseller_notes. Anything else your OneStream client sends (username, is_trial, and so on) is rejected up front with 422 validation_error so you never get a silent 200 OK on a change that did not actually apply.

The response is the full line object with the updated values, in the same shape as an element of GET /ext/lines. If you need to change a field that is not in the editable list, use the native update endpoint.

Endpoint

POST https://<your-panel-domain>/panel-api/onestream/ext/line/{uuid}/update-advanced

Authentication

X-Api-Key. X-Auth-User and Authorization: Bearer also accepted. See Authentication.

Required scope

lines:write.

Idempotency

Optional but recommended. Pass a unique rid per intended change. A retried rid with the same body returns the cached response; a retried rid with a different body returns 409 Transaction already processed.

Path parameters

Name Type Description
uuid string The opaque line UUID.

Request body

At least one editable field must be present. If none are supplied, the request is rejected with 422 validation_error.

Field Type Description
password string New line password. Admin keys only.
expire_at string, int, or null Admin keys only. Absolute expiry. ISO 8601 with offset (2026-12-31T00:00:00+00:00) is canonical. A unix integer is accepted. Pass null to remove the expiry (make the line perpetual). Passing 0 is a validation error because the engine treats exp_date = 0 as expired.
is_enabled bool Admin keys only. Set to false to disable, true to re-enable. Truthy strings ("true", "1", "yes", "on") are also accepted.
is_restreamer bool Admin keys only. Toggle the restreamer flag.
max_connections int Admin keys only. Concurrent connections cap. Clamped to [1, 100] by the underlying handler.
bouquets int[] Replaces the line's bouquet set, so send the full list you want the line to end up with, not just the delta. An empty array is ignored rather than rejected: a client that serializes the whole line object and sends "bouquets": [] gets its other fields applied and the line keeps the bouquets it has. Sent on its own, an empty array leaves nothing to edit and returns the "no editable fields provided" error. Ids the caller may not set come back in details.invalid_ids. At most 512 ids per call.
reseller_notes string The line's note. Stored verbatim, trimmed, up to 4000 characters. An empty string clears it. Same field name OneStream uses on create; a plain notes field is not read by this dialect.
rid string Idempotency key.

Fields that OneStream historically allowed but this endpoint does not accept (username, is_trial) trigger the "no editable fields provided" error even when they are the only ones sent. That is intentional: silently succeeding on a rejected field is the worst possible outcome for a billing integration.

Reseller keys

A reseller key may send only bouquets and reseller_notes here. password, expire_at, is_enabled, is_restreamer, and max_connections return 403 admin_only_field with details.fields naming them, and nothing at all is written, so a mixed body never lands half-applied. Use /ext/line/{uuid}/enable and /disable, which accept reseller keys, instead of is_enabled.

A reseller's bouquets list may only remove: every id must already be on the line, and the result must not be empty. Ids outside that set return 422 validation_error with details.invalid_ids. To widen a reseller line's bouquets, use the native POST /panel-api/v1/lines/{id}/renew with a bouquets list drawn from the package. Renew is a billing operation, not a bouquet editor: in credits mode it charges a full period at the package's price, and in every mode it moves the line's expiry date forward by the package's official duration.

Notes are per role: an admin key writes the admin note on the line and a reseller key its own reseller note. The two live side by side and never overwrite each other.

{
  "password": "new-s3cret",
  "is_enabled": false,
  "max_connections": 3,
  "expire_at": "2026-12-31T00:00:00+00:00",
  "rid": "upd-user-42-2026-08"
}

Bouquets and the note travel on this same endpoint, and both work on a reseller key:

{
  "bouquets": [2, 4],
  "reseller_notes": "Trimmed to the sports bouquets, invoice INV-42",
  "rid": "upd-user-42-bouquets-2026-08"
}

Response

200 OK with the full line object. Same shape as an element of GET /ext/lines.

{
  "line_id": "b32c1a04-11ea-4c67-8fd1-0001000000f1",
  "username": "reseller1_20260808",
  "password": "new-s3cret",
  "expire_at": "2026-12-31T00:00:00+00:00",
  "is_enabled": false,
  "is_restreamer": false,
  "is_trial": false,
  "package_id": null,
  "bouquets": [],
  "max_connections": 3,
  "reseller_notes": "",
  "mac_addr": null,
  "owner": "billing",
  "type": "regular"
}

package_id is always null in the OneStream dialect (the internal API does not surface the source package on a line row). mac_addr is always null because the product does not provision physical devices. reseller_notes is always "" in the response as well: the note you send is stored, but this dialect's line projection does not read it back, so keep your own copy if your billing system needs to display it.

Examples

cURL

curl -X POST "https://<your-panel-domain>/panel-api/onestream/ext/line/b32c1a04-11ea-4c67-8fd1-0001000000f1/update-advanced" \
  -H "X-Api-Key: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "password": "new-s3cret",
    "is_enabled": false,
    "max_connections": 3,
    "expire_at": "2026-12-31T00:00:00+00:00",
    "rid": "upd-user-42-2026-08"
  }'

PHP raw

$uuid = 'b32c1a04-11ea-4c67-8fd1-0001000000f1';
$ch = curl_init("https://<your-panel-domain>/panel-api/onestream/ext/line/{$uuid}/update-advanced");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'X-Api-Key: <your-api-key>',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'password'        => 'new-s3cret',
        'is_enabled'      => false,
        'max_connections' => 3,
        'expire_at'       => '2026-12-31T00:00:00+00:00',
        'rid'             => 'upd-user-42-2026-08',
    ]),
]);
$body   = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

Python raw

import requests

uuid_ = "b32c1a04-11ea-4c67-8fd1-0001000000f1"
r = requests.post(
    f"https://<your-panel-domain>/panel-api/onestream/ext/line/{uuid_}/update-advanced",
    headers={"X-Api-Key": "<your-api-key>"},
    json={
        "password": "new-s3cret",
        "is_enabled": False,
        "max_connections": 3,
        "expire_at": "2026-12-31T00:00:00+00:00",
        "rid": "upd-user-42-2026-08",
    },
    timeout=30,
)
r.raise_for_status()
print(r.json())

Errors

HTTP Error slug (or message) When it happens How to fix
401 Invalid API key Token is unknown, expired, disabled, or deleted. Check the token or issue a new one.
403 insufficient_scope Token lacks lines:write. Issue a key with the scope.
403 admin_only_field A reseller key sent any of password, expire_at, is_enabled, is_restreamer, max_connections. details.fields lists them and nothing was written. Drop those fields (a reseller key may send only bouquets and reseller_notes), or use an admin key.
409 Transaction already processed Same rid was reused with a different body. Use a fresh rid, or replay with the exact original body.
422 line_id not found or invalid The UUID in the URL is malformed, was not issued by this panel, or belongs to a different panel. Uniform on purpose. Verify the UUID you stored on line-creation.
422 update-advanced: none of the provided fields is editable by this API. Supported: password, expire_at, is_enabled, is_restreamer, max_connections, bouquets, reseller_notes. Body contained only rejected fields (username, is_trial), or no editable fields at all. Send at least one editable field. Use the native update endpoint for other fields.
422 validation_error bouquets is not an array of ids, carries ids the caller may not set on this line (details.invalid_ids names them), or holds more than 512 ids. An empty array never reaches the panel: the dialect drops it, and the call only fails if there was nothing else to edit. Send ids the line may have. On a reseller key, restrict them to ids the line already has.
422 validation_error reseller_notes is longer than 4000 characters or is not a string (details.field is notes). Shorten the note.
422 Invalid expire_at format: expected ISO 8601 or unix timestamp expire_at is neither a parseable ISO 8601 timestamp, a positive unix integer, nor explicit null. Send 2026-12-31T00:00:00+00:00, a positive integer, or null.
429 Rate limit exceeded The key hit its per-minute cap. Back off and retry after Retry-After seconds.
501 not_implemented A GET request was sent to this URL. Only POST is accepted.

See also