---
title: "Create a line with an explicit expiry, POST /ext/line/create-advanced"
description: "Same as /ext/line/create but lets you set the expiry with an ISO 8601 timestamp or a unix integer instead of deriving it from the package duration."
---

# Create a line with an explicit expiry, POST /ext/line/create-advanced

> [!NOTE]
> If you are starting a new integration instead of migrating, prefer the [native v1 API](/docs/?page=xai-ref-lines-create) with the [official SDKs](/docs/?page=panel-api-sdks). The OneStream dialect exists to let existing OneStream code point at Xtream AI with only a base URL change.

Create a line and pick its expiry explicitly. This is the same endpoint as [POST /ext/line/create](/docs/?page=os-ref-lines-create) with two extra fields: `expire_at` and `can_watch_adult`. Everything else (billing, ownership, bouquets, `rid` idempotency, autogenerated username or password) behaves identically.

Use this variant when your billing system needs to override the package's default duration. Typical cases: extending a promo, granting a prorated month, or seeding a trial that should end on a specific calendar date.

## Endpoint

`POST https://<your-panel-domain>/panel-api/onestream/ext/line/create-advanced`

## Authentication

Send the token as `X-Api-Key`. The legacy header `X-Auth-User` is accepted as an alias. `Authorization: Bearer <token>` is also accepted. See [Authentication](/docs/?page=panel-api-authentication).

## Required scope

`lines:write`.

## Idempotency

Optional but strongly recommended. Pass a unique `rid` in the JSON body. Same-body retries return the original response. Different-body retries return `409 Transaction already processed`.

## Request body

All the fields from [POST /ext/line/create](/docs/?page=os-ref-lines-create), plus:

| Field | Type | Required | Default | Description |
| ----- | ---- | -------- | ------- | ----------- |
| `expire_at` | string or int | no | derived from package | Absolute expiry. ISO 8601 with offset (`2026-12-31T00:00:00+00:00`) is the canonical form. A unix integer (as string or number) is accepted for legacy clients. Malformed values return `422 validation_error` rather than silently falling back to the package duration. |
| `can_watch_adult` | bool | no | false | Adult-content flag on the resulting line. |

`0` is not the way to say "no expiration". The engine treats `exp_date = 0` as expired. To create a line with no expiration use [POST /ext/line/{uuid}/update-advanced](/docs/?page=os-ref-line-update-advanced) with `expire_at: null` after creation.

```json
{
  "package": 42,
  "member_id": 100,
  "username": "reseller1_vip_20260808",
  "password": "s3cret-pw",
  "expire_at": "2026-12-31T00:00:00+00:00",
  "can_watch_adult": true,
  "rid": "invoice-INV-2026-00815"
}
```

## Response

`200 OK` with the same compact shape as the plain create. `expire_at` reflects the value you passed, not the package default.

```json
{
  "line_id": "5f14e08b-a731-4c31-8bcd-0001000000f2",
  "expire_at": "2026-12-31T00:00:00+00:00",
  "transaction_amount": 0,
  "rid": "invoice-INV-2026-00815"
}
```

## Examples

### cURL

```bash
curl -X POST https://<your-panel-domain>/panel-api/onestream/ext/line/create-advanced \
  -H "X-Api-Key: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "package": 42,
    "member_id": 100,
    "username": "reseller1_vip_20260808",
    "password": "s3cret-pw",
    "expire_at": "2026-12-31T00:00:00+00:00",
    "can_watch_adult": true,
    "rid": "invoice-INV-2026-00815"
  }'
```

### PHP raw

```php
$ch = curl_init('https://<your-panel-domain>/panel-api/onestream/ext/line/create-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([
        'package'         => 42,
        'member_id'       => 100,
        'username'        => 'reseller1_vip_20260808',
        'password'        => 's3cret-pw',
        'expire_at'       => '2026-12-31T00:00:00+00:00',
        'can_watch_adult' => true,
        'rid'             => 'invoice-INV-2026-00815',
    ]),
]);
$body   = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
```

### Python raw

```python
import requests

r = requests.post(
    "https://<your-panel-domain>/panel-api/onestream/ext/line/create-advanced",
    headers={"X-Api-Key": "<your-api-key>"},
    json={
        "package": 42,
        "member_id": 100,
        "username": "reseller1_vip_20260808",
        "password": "s3cret-pw",
        "expire_at": "2026-12-31T00:00:00+00:00",
        "can_watch_adult": True,
        "rid": "invoice-INV-2026-00815",
    },
    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. |
| 402 | `Insufficient credits balance` | Reseller in credits mode lacks credits for the package. | Top up credits, or pick a cheaper package. |
| 403 | `admin_only_field` | Reseller key sent `member_id`. | Remove `member_id`. |
| 403 | `insufficient_scope` | Token lacks `lines:write`. | Issue a key with the scope. |
| 403 | `package_not_accessible` | Reseller cannot sell from that package. | Use a package inside the reseller's member group. |
| 409 | `Transaction already processed` | Same `rid` was reused with a different body. | Use a fresh `rid`, or replay with the exact original body. |
| 422 | `Invalid expire_at format: expected ISO 8601 or unix timestamp` | `expire_at` is neither a parseable ISO 8601 timestamp nor a positive unix integer. | Send `2026-12-31T00:00:00+00:00` or a positive integer. |
| 422 | `validation_error` | Missing `package`, admin key without `member_id`, unknown `package`, unknown `member_id`, username collision, bouquet not accessible. | See `details.field` in the response. |
| 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

- [Create a line, POST /ext/line/create](/docs/?page=os-ref-lines-create)
- [Update a line, POST /ext/line/{uuid}/update-advanced](/docs/?page=os-ref-line-update-advanced)
- [OneStream compatibility overview](/docs/?page=panel-api-onestream-compatibility)
- [Native lines API](/docs/?page=xai-ref-lines-list)
