Using the official SDKs? Every endpoint below is a one-line typed method call — the HTTP details on this page are handled for you.

What a line is

A line is a subscriber account on your panel. It has a username and password that end customers plug into an IPTV app, an expiry date, a set of bouquets (channel groups) it is allowed to see, a maximum number of concurrent connections, and a few flags (is_trial, is_restreamer). Every line belongs to a specific owner: an admin or a reseller. Resellers only ever see, mutate, or bill for their own lines.

This page documents every endpoint under /panel-api/v1/lines/*. Requests need either the lines:read or lines:write scope depending on the operation. See Authentication for how to obtain a token.

Every write operation (create, update, enable, disable, renew, reset password, delete) requires an Idempotency-Key header. See Rate limits & Idempotency for the safe-retry contract.

The line lifecycle

A line moves through a small number of well-defined states over its life:

  1. Create with POST /lines. You pick the package (which decides duration, credit cost, default bouquets, default connections, and the trial flag) and, if you are an admin, the member_id of the owner reseller. The line is created with enabled=true and admin_enabled=true. On a reseller key, credits are deducted atomically before the row is written, and refunded automatically if the create fails.
  2. List and get with GET /lines and GET /lines/{id}. Reseller keys are auto-scoped to their own member_id.
  3. Enable and disable with POST /lines/{id}/enable and POST /lines/{id}/disable. disable never charges. enable re-checks slot capacity on reseller keys in users billing mode, so a reseller cannot use disable-then-enable to sneak past the cap.
  4. Renew with POST /lines/{id}/renew to push exp_date forward by the package's official duration. In credits mode this deducts official_credits; in users mode renewals are free.
  5. Reset the password at any time with POST /lines/{id}/reset-password (returns the new password in the response).
  6. Update operator-level fields (admin only) with POST /lines/{id}/update. This is a partial update: only the fields you send are written.
  7. Inspect live connections with GET /lines/{id}/connections to see who is streaming what, from where.
  8. Delete with POST /lines/{id}/delete. Bouquet assignments and contact-info rows are cleaned up in the same transaction. Deletion does not refund credits, matching the panel UI's behavior.

The two flags enabled and admin_enabled are separate on purpose: enabled is the reseller-visible toggle, admin_enabled is a hard override the admin can flip to block a line regardless of what the reseller does. The Panel API surfaces both, but only POST /lines/{id}/update (admin-only) can touch admin_enabled; the enable/disable endpoints only move enabled.

The line object

Every endpoint that returns a line uses this shape:

{
  "id": 12345,
  "username": "u_ab12cd34",
  "password": "9f3c1a77",
  "member_id": 42,
  "exp_date": 1793520000,
  "max_connections": 2,
  "is_trial": false,
  "is_restreamer": false,
  "enabled": true,
  "admin_enabled": true,
  "bouquets": [1, 4, 9],
  "created_at": 1785984000
}

Field notes:

Field Type Notes
id int Panel-wide numeric id. Stable for the life of the line.
username string Autogenerated as u_<8hex> (or trial_<8hex> for trials) if you don't send one.
password string Autogenerated (8 hex chars) if omitted. Returned in clear in every line response.
member_id int Numeric id of the owner (admin or reseller). Reseller keys always see their own id here.
exp_date int or null Expiry as a UTC Unix epoch. null means the line never expires (perpetual).
max_connections int Concurrent-connection cap, in the range [1, 100].
is_trial bool True if the line was created from a trial package.
is_restreamer bool True if this line is allowed to restream through the panel.
enabled bool Reseller-visible toggle.
admin_enabled bool Admin override. If false, the line is blocked no matter what enabled says.
bouquets int[] Bouquet ids assigned to the line.
created_at int or null Creation timestamp (UTC Unix epoch).

Endpoints

The PHP and Python examples below assume a $client / client already constructed as shown on the SDKs page (base URL plus API key), so each endpoint shows only the call itself. The SDK generates the Idempotency-Key header for every write automatically; pass idempotencyKey / idempotency_key yourself only when you want to derive it from a business ID. All line methods live under $client->lines / client.lines.

POST /lines — create a line

Create a new subscriber line.

Scope: lines:write. Idempotency: required.

Body fields

Field Type Required Notes
package_id int yes Must exist. Determines default duration, credit cost, default bouquets, default max_connections, and whether the line is a trial.
member_id int admin only, required The reseller (or admin) that will own the line. Reseller keys must NOT send this: the owner is forced to the key's own reseller id, and sending this field returns 403 admin_only_field.
username string no Autogenerated if omitted. Must be unique panel-wide.
password string no Autogenerated (8 hex chars) if omitted. Reseller keys whose member group has allow_change_pass=0 cannot pick a password (returns 403 password_change_not_allowed).
bouquets int[] no Falls back to the package's default bouquets if omitted or empty. On reseller keys, every bouquet id must be visible to the reseller's member group; unknown ids return 422 validation_error with details.invalid_ids.
is_trial bool no Only valid if the package itself is a trial package. Sending true on a non-trial package returns 422 trial_flag_requires_trial_package. A trial package always forces is_trial=true even if you don't send it.
email, notes string no Stored on the line's contact-info row. Best-effort: a write failure here logs but does not fail the create. Reseller keys write to reseller_user_contact_info, admin keys to admin_user_contact_info.
exp_date int (UTC epoch) admin only Custom expiry. Must be in the future and within 5 years. If omitted, expiry comes from the package's duration.
max_connections int admin only Override the package's default. Clamped to [1, 100].
is_restreamer bool admin only Override the package's default.
allowed_ips string[] admin only IPv4 allow-list. Up to 50 entries. Invalid entries are dropped silently.
allowed_ua string[] admin only User-Agent allow-list. Up to 50 entries, each capped at 500 characters.
is_isplock bool admin only Lock the line to the first ISP that connects.

Admin-only fields: member_id, exp_date, max_connections, is_restreamer, allowed_ips, allowed_ua, is_isplock. If a reseller key sends any of these, the request is rejected with 403 admin_only_field and the offending field names come back in details.fields.

Order of validation (all before any write):

  1. Package exists.
  2. Reseller only: the package is in a member group the reseller can sell from.
  3. is_trial matches the package.
  4. Reseller only: subscription is still active (billing_expires not in the past). Admin keys are exempt here, so the admin can rescue an expired reseller's customer manually.
  5. Reseller + trial: the reseller's trial quota for the current window is not exceeded.
  6. Reseller + users billing mode + non-trial: slot capacity (both the reseller's own cap and any ancestor cap) is not exceeded.
  7. Reseller + credits billing mode: enough credits to cover the package cost.
  8. Member-group permission checks for password and is_isplock.
  9. Bouquet ids are all visible to the reseller.

Credit deduction is atomic. In credits mode the deduction happens before the row insert, using a conditional UPDATE. If the deduction fails you get 402 insufficient_credits. If the insert fails afterwards (for example, a username collision) the credits are refunded automatically.

curl:

curl -H "Authorization: Bearer $TOKEN" \
     -H "Idempotency-Key: $(uuidgen)" \
     -H "Content-Type: application/json" \
     -d '{
       "package_id": 1,
       "member_id": 42,
       "username": "johndoe",
       "password": "s3cret!",
       "bouquets": [1, 4, 9],
       "email": "johndoe@example.com",
       "notes": "created from WHMCS order #4712"
     }' \
     https://<your-panel-domain>/panel-api/v1/lines
$line = $client->lines->create(
    packageId: 1,
    memberId:  42,
    username:  'johndoe',
    password:  's3cret!',
    bouquets:  [1, 4, 9],
    email:     'johndoe@example.com',
    notes:     'created from WHMCS order #4712',
);
echo $line->id, ' ', $line->username, PHP_EOL;
line = client.lines.create(
    package_id=1,
    member_id=42,
    username="johndoe",
    password="s3cret!",
    bouquets=[1, 4, 9],
    email="johndoe@example.com",
    notes="created from WHMCS order #4712",
)
print(line.id, line.username)

Response (201): a full line object.

Errors:

HTTP Slug Meaning
400 missing_idempotency_key Header not sent.
402 billing_expired Reseller's subscription has expired.
402 trial_quota_exceeded Reseller hit their trial-creation cap for the current window.
402 slot_limit_exceeded Reseller's own user cap would be exceeded.
402 ancestor_cap_reached A parent reseller's cap would be exceeded.
402 insufficient_credits Not enough credits to pay for the package.
403 admin_only_field Reseller sent a field reserved to admin keys.
403 package_not_accessible Reseller cannot sell from that package.
403 password_change_not_allowed Reseller's member group forbids picking a password.
403 isplock_not_allowed Reseller's member group forbids ISP-lock.
409 idempotency_conflict / idempotency_in_flight Retry with a different key, or wait for the in-flight request to finish.
422 validation_error Missing / malformed field, unknown package, unknown member_id, username collision, bouquet not accessible.
422 trial_flag_requires_trial_package is_trial=true on a non-trial package.

GET /lines — list lines

List lines with cursor-based pagination.

Scope: lines:read.

Query parameters

Param Type Notes
limit int Page size. Clamped to [1, 100], default 50.
cursor int Keyset cursor: the API returns lines with id > cursor, ascending. Use next_cursor from the previous response to walk the whole list.
is_trial bool Filter by trial flag. Accepts true / false / 1 / 0 / yes / no / on / off, case-insensitive.
enabled bool Same boolean parser as is_trial.
username string Exact match.
password string Exact match. Combined with username, this gives you the "find by credentials" query billing systems use to reconcile.

Reseller keys are automatically scoped to their own member_id: they cannot see lines that belong to another owner, regardless of any filter they send.

When you filter by password, we recommend sending the value in the request body (as an alternative supported form) or, better, calling GET /lines/{id} once you already know the id. A password in a query string can leak into intermediate access logs (your load balancer, a corporate proxy). Our nginx logs strip the query string for the Panel API location, but you should not assume the whole path from client to server does the same.

curl:

curl -H "Authorization: Bearer $TOKEN" \
     "https://<your-panel-domain>/panel-api/v1/lines?limit=50&enabled=true&cursor=0"
$page = $client->lines->list(limit: 50, enabled: true);
foreach ($page->items as $line) {
    echo $line->id, ' ', $line->username, ' ', $line->enabled ? 'on' : 'off', PHP_EOL;
}
if ($page->nextCursor !== null) {
    echo 'next page cursor: ', $page->nextCursor, PHP_EOL;
}
page = client.lines.list(limit=50, enabled=True)
for line in page.items:
    print(line.id, line.username, line.enabled)
if page.next_cursor is not None:
    print("next page cursor:", page.next_cursor)

Response (200):

{
  "items": [
    { "id": 12345, "username": "u_ab12cd34", "...": "..." },
    { "id": 12346, "username": "u_ff2233aa", "...": "..." }
  ],
  "next_cursor": 12346
}

next_cursor is null on the last page. Pass it back as the cursor query parameter to fetch the next slice.

GET /lines/{id} — fetch one line

Return the full line object for a single id.

Scope: lines:read.

curl:

curl -H "Authorization: Bearer $TOKEN" \
     https://<your-panel-domain>/panel-api/v1/lines/12345
$line = $client->lines->get(12345);
echo $line->id, ' ', $line->username, ' expires ',
     $line->expDate?->format(DATE_ATOM) ?? 'never', PHP_EOL;
line = client.lines.get(12345)
print(line.id, line.username, "expires",
      line.exp_date.isoformat() if line.exp_date else "never")

Response (200): a full line object.

Errors: 404 not_found. Cross-tenant reads also return 404, not 403, so a reseller cannot use this endpoint to check whether an id exists in another reseller's book of business.

POST /lines/{id}/update — partial update (admin only)

Patch a subset of fields on an existing line. Every field is optional; only the fields you send are written.

Scope: lines:write. Admin key required, reseller keys get 403 admin_only_endpoint. Idempotency: required.

Accepted fields

Field Type Notes
password string Set a new password (no random generation, use /reset-password for that).
exp_date int, or null UTC epoch. Sending null explicitly makes the line perpetual (no expiry). Omitting the field leaves the current value alone.
max_connections int Clamped to [1, 100].
is_restreamer bool Flip the restreamer flag.
enabled bool Reseller-visible toggle.
admin_enabled bool Hard admin override. Only settable through this endpoint.
allowed_ips string[] IPv4 allow-list, up to 50 entries.
allowed_ua string[] User-Agent allow-list, up to 50 entries.

Unlike the create endpoint, exp_date here is not range-validated. You can move a line arbitrarily far into the future or back in time. Use with care.

curl:

curl -H "Authorization: Bearer $TOKEN" \
     -H "Idempotency-Key: $(uuidgen)" \
     -H "Content-Type: application/json" \
     -d '{"max_connections": 4, "is_restreamer": true}' \
     https://<your-panel-domain>/panel-api/v1/lines/12345/update
$line = $client->lines->update(
    id:             12345,
    maxConnections: 4,
    isRestreamer:   true,
);
echo $line->maxConnections, PHP_EOL;
line = client.lines.update(
    id=12345,
    max_connections=4,
    is_restreamer=True,
)
print(line.max_connections)

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 — e.g. $client->lines->update(id: 12345, expDate: null) / client.lines.update(id=12345, exp_date=None).

Response (200): a full line object with the updated fields.

Errors: 400 missing_idempotency_key · 403 admin_only_endpoint · 404 not_found · 409 idempotency codes · 500 internal_error.

POST /lines/{id}/enable

Flip the enabled flag to true.

Scope: lines:write. Idempotency: required.

For reseller keys in users billing mode with a non-trial line, this endpoint re-checks slot capacity before enabling. This closes the loophole where a reseller could disable an old line and enable a new one to sneak past their user cap.

curl:

curl -X POST \
     -H "Authorization: Bearer $TOKEN" \
     -H "Idempotency-Key: $(uuidgen)" \
     https://<your-panel-domain>/panel-api/v1/lines/12345/enable
$line = $client->lines->enable(12345);
echo $line->enabled ? 'enabled' : 'still disabled', PHP_EOL;
line = client.lines.enable(12345)
print(line.enabled)

Response (200): a full line object with "enabled": true.

Errors: 400 missing_idempotency_key · 402 slot_limit_exceeded / ancestor_cap_reached (reseller, users mode) · 404 not_found · 409 idempotency codes · 500 internal_error.

POST /lines/{id}/disable

Flip the enabled flag to false. Never charges, never rejects on billing.

Scope: lines:write. Idempotency: required.

curl:

curl -X POST \
     -H "Authorization: Bearer $TOKEN" \
     -H "Idempotency-Key: $(uuidgen)" \
     https://<your-panel-domain>/panel-api/v1/lines/12345/disable
$line = $client->lines->disable(12345);
echo $line->enabled ? 'still enabled' : 'disabled', PHP_EOL;
line = client.lines.disable(12345)
print(line.enabled)

Response (200): a full line object with "enabled": false.

Errors: 400 missing_idempotency_key · 404 not_found · 409 idempotency codes · 500 internal_error.

POST /lines/{id}/renew

Extend the expiry of a line by the package's official duration.

Scope: lines:write. Idempotency: required.

Body

Field Type Required Notes
package_id int yes Must exist. Cannot be a trial package (returns 422 renew_with_trial_package_not_allowed). Reseller keys must have access to the package (403 package_not_accessible otherwise).

Behavior:

  • The new expiry is max(current_exp_date, now) + package.official_duration. If the line already expired, the countdown starts from now. If it is still active, the extension is added on top of the current expiry (no lost days).
  • admin_enabled and enabled are both set to true in the same statement, so a renew reactivates a previously disabled or admin-blocked line.
  • Reseller in credits mode: deducts package.official_credits before the update. Failure to deduct returns 402 insufficient_credits. If the update fails after the deduction, the credits are refunded.
  • Reseller in users mode: renewals are free (a slot is already accounted for as long as the line exists).
  • Perpetual lines (exp_date == null) cannot be renewed: renewing would set an expiry and effectively degrade the line. The endpoint returns 422 line_has_no_expiry. Use POST /lines/{id}/update with a chosen exp_date if you really want to convert a perpetual line into a timed one.

curl:

curl -H "Authorization: Bearer $TOKEN" \
     -H "Idempotency-Key: $(uuidgen)" \
     -H "Content-Type: application/json" \
     -d '{"package_id": 1}' \
     https://<your-panel-domain>/panel-api/v1/lines/12345/renew
$line = $client->lines->renew(id: 12345, packageId: 1);
echo 'new expiry ', $line->expDate?->format(DATE_ATOM), PHP_EOL;
line = client.lines.renew(id=12345, package_id=1)
print("new expiry", line.exp_date.isoformat() if line.exp_date else None)

Response (200): a full line object with the new exp_date.

Errors: 400 missing_idempotency_key · 402 billing_expired / insufficient_credits · 403 package_not_accessible · 404 not_found · 409 idempotency codes · 422 validation_error (unknown package) / renew_with_trial_package_not_allowed / line_has_no_expiry · 500 internal_error.

POST /lines/{id}/reset-password

Rotate a line's password.

Scope: lines:write. Idempotency: required.

Body

Field Type Required Notes
password string no If omitted, the API generates a random 8-hex-character password.

Reseller keys whose member group has allow_change_pass=0 cannot use this endpoint (403 password_change_not_allowed).

curl:

curl -H "Authorization: Bearer $TOKEN" \
     -H "Idempotency-Key: $(uuidgen)" \
     -H "Content-Type: application/json" \
     -d '{}' \
     https://<your-panel-domain>/panel-api/v1/lines/12345/reset-password
// resetPassword returns the new password as a string, not a Line.
$newPassword = $client->lines->resetPassword(12345);
echo $newPassword, PHP_EOL;  // e.g. "9f3c1a77"
# reset_password returns the new password as a str, not a Line.
new_password = client.lines.reset_password(12345)
print(new_password)  # e.g. "9f3c1a77"

To set a specific password instead of a random one, pass it: $client->lines->resetPassword(12345, 's3cret!') / client.lines.reset_password(12345, "s3cret!").

Response (200): a compact shape, not the full line object:

{"id": 12345, "password": "9f3c1a77"}

If you need the full line back, call GET /lines/{id} afterwards.

Errors: 400 missing_idempotency_key · 403 password_change_not_allowed · 404 not_found · 409 idempotency codes · 500 internal_error.

POST /lines/{id}/delete

Delete a line and cascade-clean its related rows (bouquet assignments, contact info).

Scope: lines:write. Idempotency: required.

Reseller keys are subject to the member_groups.delete_users permission: if the reseller's group is not allowed to delete customers, the endpoint returns 403 delete_not_allowed. This mirrors the guard the reseller UI applies, so a POST from an API integration cannot bypass what the UI itself would block.

Deletion does not refund credits, matching the panel UI. In users billing mode the slot is released automatically (the active-users count reads live from the users table).

curl:

curl -X POST \
     -H "Authorization: Bearer $TOKEN" \
     -H "Idempotency-Key: $(uuidgen)" \
     https://<your-panel-domain>/panel-api/v1/lines/12345/delete
// delete returns a bool: true once the panel confirms the row is gone.
$deleted = $client->lines->delete(12345);
echo $deleted ? 'gone' : 'still there', PHP_EOL;
# delete returns a bool: True once the panel confirms the row is gone.
deleted = client.lines.delete(12345)
print(deleted)  # True

Response (200):

{"id": 12345, "username": "u_ab12cd34", "deleted": true}

The API verifies the row actually disappeared before reporting success. If the delete cascade fails partway through, the endpoint returns 500 delete_failed instead of a false 200, so your billing system does not mark a customer inactive while their line is still active on the panel.

Errors: 400 missing_idempotency_key · 403 delete_not_allowed (reseller only) · 404 not_found · 409 idempotency codes · 500 delete_failed / internal_error.

GET /lines/{id}/connections

List live streaming connections for a single line. Useful for kick-per-user diagnostics, showing "who is watching what" in a support UI, or feeding a fraud-detection pipeline.

Scope: lines:read.

Returns up to 200 connections, ordered by started_at descending. There is no pagination cursor: a single line rarely has more than a handful of active connections at once.

curl:

curl -H "Authorization: Bearer $TOKEN" \
     https://<your-panel-domain>/panel-api/v1/lines/12345/connections
// connections returns a plain array of Connection objects (no cursor).
$connections = $client->lines->connections(12345);
foreach ($connections as $c) {
    echo $c->clientIp, ' ', $c->clientCountry, ' ', $c->contentName, ' ', $c->elapsedSec, "s\n";
}
# connections returns a plain list of Connection objects (no cursor).
connections = client.lines.connections(12345)
for c in connections:
    print(c.client_ip, c.client_country, c.content_name, c.elapsed_sec, "s")

Response (200):

{
  "items": [
    {
      "connection_id": 987654,
      "content_type": "live",
      "content_id": 4211,
      "content_name": "ESPN HD",
      "started_at": 1785984000,
      "elapsed_sec": 132,
      "client_ip": "203.0.113.42",
      "client_country": "US"
    },
    {
      "connection_id": 987655,
      "content_type": "movie",
      "content_id": 88012,
      "content_name": "Breaking Bad S01E01",
      "started_at": 1785983900,
      "elapsed_sec": 232,
      "client_ip": "203.0.113.42",
      "client_country": "US",
      "is_serie": true
    }
  ]
}

Field notes:

  • content_type is normalized to "live" or "movie". Series episodes are reported as "movie" with an additional is_serie: true flag.
  • content_id and content_name are null / empty if the content was deleted after the connection started (rare, but the join is a LEFT JOIN so the connection is still surfaced).
  • elapsed_sec is computed server-side, so successive calls give you a monotonically increasing value without clock drift concerns on your end.
  • client_ip and client_country come from the streaming server's own connection log, which uses a MaxMind GeoIP2 database for the country code.

Fields that we deliberately do NOT expose here: server ip, session id, user-agent. If you need those for a specific audit, tell us the use case and we will decide whether to add them behind an opt-in scope.

Errors: 404 not_found.

Cross-tenant isolation

Every endpoint on this page enforces the same rule: a reseller key can never see or mutate a line whose member_id is not the key's own reseller id. This includes:

  • GET /lines auto-filters by member_id = <caller>.
  • GET /lines/{id}, POST /lines/{id}/*, GET /lines/{id}/connections all resolve the line, check ownership, and return 404 not_found on any mismatch.
  • We return 404, not 403, on cross-tenant reads. A 403 would confirm that the requested id exists, which is enough for an attacker to enumerate the size of another reseller's book of business inside the same panel.

Admin keys have no such filter: they see and mutate every line, in every reseller's book. Give the admin scope only to integrations that genuinely need panel-wide reach (billing reconciliation, migration tooling, incident response). For everything else, issue a reseller key with the smallest scope that gets the job done.

Common errors

The full table of Panel API error slugs, HTTP codes, and remediation lives in Errors. The subset your integration is most likely to hit on this resource:

HTTP Slug When
400 missing_idempotency_key Any write without the header. Send a fresh key on each new attempt; reuse only when retrying the same logical operation.
402 insufficient_credits Reseller ran out of credits mid-create or mid-renew. Top up and retry with a fresh idempotency key.
402 billing_expired Reseller's subscription lapsed. Renew the reseller first, then retry.
402 slot_limit_exceeded / ancestor_cap_reached Reseller (or an ancestor) hit their user cap. Raise the cap or delete inactive lines.
402 trial_quota_exceeded Reseller hit their trial-creation quota for the current window. Wait for the window to roll or raise the cap.
403 admin_only_endpoint Reseller called /lines/{id}/update. Use an admin key.
403 admin_only_field Reseller sent a field reserved to admin keys (member_id, exp_date, max_connections, is_restreamer, allowed_ips, allowed_ua, is_isplock). Drop the field or use an admin key.
403 package_not_accessible Reseller cannot sell from that package. Add the package to the reseller's member group, or pick another package.
403 password_change_not_allowed Reseller's member group forbids picking / resetting passwords. Change the group permission or let the API autogenerate.
403 delete_not_allowed Reseller's member group forbids deleting customers.
404 not_found The line does not exist, or (reseller only) it exists but belongs to someone else.
409 idempotency_in_flight An earlier request with the same key is still processing. Wait and retry.
409 idempotency_conflict An earlier request with the same key resolved with a different body. Generate a fresh key.
422 validation_error Missing / malformed field, unknown package or member id, username collision, bouquets not accessible. details usually points to the offending field.
422 trial_flag_requires_trial_package Sent is_trial=true on a non-trial package.
422 renew_with_trial_package_not_allowed Passed a trial package to /renew.
422 line_has_no_expiry Tried to renew a perpetual line.
429 rate_limited Per-key or per-IP budget exceeded. Honor the Retry-After header.

See also

  • Overview — the whole Panel API in one page.
  • Authentication — token shape, scopes, IP allow-lists.
  • Rate limits & Idempotency — safe-retry semantics, budgets.
  • Catalog — packages, bouquets, streams, and VODs you can attach to a line.
  • Resellers — the owners of the lines you create with member_id.
  • Errors — the full slug table for every endpoint.