Create a sub-reseller, POST /ext/user/create

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.

Create a sub-reseller. Admin keys can create sub-resellers freely. Reseller keys can create sub-resellers only if their member group has create_sub_resellers = 1, and the panel operator has configured a sub-reseller setup for that group. On reseller keys the new sub-reseller inherits the parent's billing mode (credits or users) and is charged the group's create_sub_resellers_price in credits mode.

The OneStream body shape is honored, including the optional password_confirmation field. If your client sends both password and password_confirmation and they do not match, the request is rejected before any row is written.

Endpoint

POST https://<your-panel-domain>/panel-api/onestream/ext/user/create

Authentication

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

Required scope

resellers:write.

Idempotency

Optional but recommended. A retried rid returns the cached response instead of creating a second sub-reseller. In credits mode this is what prevents a network retry from charging the caller twice.

Request body

Field Type Required Default Description
name (or username) string yes Login name for the new sub-reseller. Must be unique panel-wide.
email string no Contact email stored on the reg-user row.
password string yes Plain-text password. Stored as the panel stores every user credential.
password_confirmation string no If sent, must equal password. If omitted, the check is skipped.
credits number no 0 Initial credit balance. On reseller keys this field is ignored: the child is created with 0 credits and the caller must top up separately using POST /ext/user/{id}/credit.
notes string no Free-form note stored on the reg-user row.
member_group_id int admin, yes Reseller group the new user joins. Admin keys must pick a group with is_reseller = 1. On reseller keys the group is forced to the value the panel operator configured for the parent's group, and any value sent in the body is discarded.
rid string no Idempotency key.

The OneStream API does not expose member_group_id in its documented shape. If you are migrating a client that never sent this field, add it now for admin keys; a plain create without a group name is rejected with 422 Member group not found.

{
  "name": "reseller_billing",
  "email": "reseller_billing@example.com",
  "password": "topsecret",
  "password_confirmation": "topsecret",
  "credits": 100,
  "notes": "created by billing system",
  "member_group_id": 4,
  "rid": "user-create-billing-1"
}

Response

200 OK with the new sub-reseller's internal integer id and the echoed rid. Sub-resellers do not use the opaque UUID format that lines use, because the integer id is safe to expose (it is not a target of enumeration attacks the way live subscriber lines are).

{
  "id": 5555,
  "rid": "user-create-billing-1"
}

Store the returned id next to your own billing record; it is the value you pass to POST /ext/user/{id}/update and POST /ext/user/{id}/credit later on.

Examples

cURL

curl -X POST https://<your-panel-domain>/panel-api/onestream/ext/user/create \
  -H "X-Api-Key: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "reseller_billing",
    "email": "reseller_billing@example.com",
    "password": "topsecret",
    "password_confirmation": "topsecret",
    "credits": 100,
    "notes": "created by billing system",
    "member_group_id": 4,
    "rid": "user-create-billing-1"
  }'

PHP raw

$ch = curl_init('https://<your-panel-domain>/panel-api/onestream/ext/user/create');
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([
        'name'                  => 'reseller_billing',
        'email'                 => 'reseller_billing@example.com',
        'password'              => 'topsecret',
        'password_confirmation' => 'topsecret',
        'credits'               => 100,
        'notes'                 => 'created by billing system',
        'member_group_id'       => 4,
        'rid'                   => 'user-create-billing-1',
    ]),
]);
$body   = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

Python raw

import requests

r = requests.post(
    "https://<your-panel-domain>/panel-api/onestream/ext/user/create",
    headers={"X-Api-Key": "<your-api-key>"},
    json={
        "name": "reseller_billing",
        "email": "reseller_billing@example.com",
        "password": "topsecret",
        "password_confirmation": "topsecret",
        "credits": 100,
        "notes": "created by billing system",
        "member_group_id": 4,
        "rid": "user-create-billing-1",
    },
    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 the credits configured as create_sub_resellers_price. Top up credits before creating.
403 insufficient_scope Token lacks resellers:write. Issue a key with the scope.
403 create_sub_resellers_not_allowed Reseller key belongs to a group with create_sub_resellers = 0, or the panel operator has not configured a sub-reseller setup for the group. Ask the panel operator to enable sub-reseller creation for the 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 password and password_confirmation do not match Both fields were sent and their values differ. Send them equal, or omit password_confirmation to skip the check.
422 Member group not found Admin key did not pass member_group_id, or the value points to a non-reseller group. Pass a valid reseller group id (is_reseller = 1).
422 validation_error Missing name or password, username collision, malformed email. 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