Sub-users

9 Aufrufe Markdown

The five endpoints that manage the limited-permission accounts reaching a client's panel.

Overview

A sub-user is a second person who reaches a client's panel with limited permissions: the accountant sees only invoices, the technical team only services.

The permission set is fixed and holds ten values. The owner's own profile, billing contacts and e-mail history cannot be handed over; those stay with the account itself.

Reference

Listing the Sub-users

get/api/v1/admin/clients/{id}/subusers
Clients/GetClientSubusers admin

Returns every sub-user the client has.

Response fields data[] — 12
idintSub-user id.
emailstringE-mail address.
labelstring | nullA label. For a department or role name.
statusstringpending is awaiting the invite, active is live, inactive is switched off.
permissionsarrayThe permissions granted.
view_servicespermissionSees the services.
view_passwordspermissionSees service passwords.
allow_ssopermissionSigns in to a service panel in one click.
view_domainspermissionSees the domains.
manage_domainspermissionManages the domains.
view_invoicespermissionSees the invoices.
view_ticketspermissionSees the support tickets.
view_affiliatepermissionSees the affiliate page.
view_resellerpermissionSees the reseller page.
new_orderspermissionPlaces new orders.
email_notificationsintE-mail notification preference. A bitmask.
sms_notificationsintSMS notification preference. A bitmask.
linked_user_idintId of the real client account it is linked to. 0 when there is none.
linked_user_namestring | nullName of the linked account.
invited_atstring | nullWhen the invite went out.
accepted_atstring | nullWhen the invite was accepted.
created_atstring | nullWhen the record was created.
Errors 2
not_found404No such client or sub-user.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/clients/42/subusers' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/clients/42/subusers', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/subusers');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->GetClientSubusers(['id' => 42]);
Response
{
  "data": [
    {
      "id": 4,
      "email": "[email protected]",
      "label": "Accounting",
      "status": "active",
      "permissions": ["view_invoices", "view_services"],
      "email_notifications": 0,
      "sms_notifications": 0,
      "linked_user_id": 31,
      "linked_user_name": "John Doe",
      "invited_at": "2026-06-01 10:00:00",
      "accepted_at": "2026-06-02 09:12:00",
      "created_at": "2026-06-01 10:00:00"
    }
  ]
}

Adding a Sub-user

post/api/v1/admin/clients/{id}/subusers
Clients/CreateClientSubuser admin 201

Adds a sub-user to the client. The record starts out pending.

Body 6
emailstringrequiredE-mail address. It has to differ from the owner's and not be in use already.
labelstringA label.
permissionsarrayThe permissions to grant. An unrecognised key is dropped without a word.
view_servicespermissionSees the services.
view_passwordspermissionSees service passwords.
allow_ssopermissionSigns in to a service panel in one click.
view_domainspermissionSees the domains.
manage_domainspermissionManages the domains.
view_invoicespermissionSees the invoices.
view_ticketspermissionSees the support tickets.
view_affiliatepermissionSees the affiliate page.
view_resellerpermissionSees the reseller page.
new_orderspermissionPlaces new orders.
email_notificationsintE-mail notification preference. Defaults to 0.
sms_notificationsintSMS notification preference. Defaults to 0.
send_inviteboolSends the invite e-mail. On by default.
Response fields 201 — data
dataobjectThe sub-user created. Same shape as the listing endpoint.
Errors 6
not_found404No such client or sub-user.
email_invalid422The e-mail is not valid.
email_is_owner422The e-mail is the owner's own.
email_exists422A sub-user with this e-mail already exists.
subuser_add_failed500The insert failed.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/subusers' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","label":"Accounting","permissions":["view_invoices","view_services"]}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/subusers', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: '[email protected]',
    label: 'Accounting',
    permissions: ['view_invoices', 'view_services'],
    send_invite: true,
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/subusers');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'email'       => '[email protected]',
        'label'       => 'Accounting',
        'permissions' => ['view_invoices', 'view_services'],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->CreateClientSubuser([
    'id'          => 42,
    'email'       => '[email protected]',
    'permissions' => ['view_invoices', 'view_services'],
]);

// Read the granted permissions back: an unrecognised key is dropped silently.
$granted = $response['data']['permissions'];

Updating a Sub-user

patch/api/v1/admin/clients/{id}/subusers/{subuser_id}
Clients/UpdateClientSubuser admin can link an account

Updates the fields you send and leaves the rest alone.

Body 5
labelstringThe label.
statusstringThe new status. An unrecognised value keeps the current one.
permissionsarrayThe permission list. If you send it, the list is written whole.
view_servicespermissionSees the services.
view_passwordspermissionSees service passwords.
allow_ssopermissionSigns in to a service panel in one click.
view_domainspermissionSees the domains.
manage_domainspermissionManages the domains.
view_invoicespermissionSees the invoices.
view_ticketspermissionSees the support tickets.
view_affiliatepermissionSees the affiliate page.
view_resellerpermissionSees the reseller page.
new_orderspermissionPlaces new orders.
email_notificationsintE-mail notification preference.
sms_notificationsintSMS notification preference.
Response fields data
dataobjectThe sub-user as it now stands. Same shape as the listing endpoint.
Errors 2
not_found404No such client or sub-user.
insufficient_scope403The key lacks the required scope.
Request
curl -X PATCH 'https://panel.example.com/api/v1/admin/clients/42/subusers/5' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"status":"active"}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/subusers/5', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ status: 'active' }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/subusers/5');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['status' => 'active']),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->UpdateClientSubuser([
    'id'         => 42,
    'subuser_id' => 5,
    'status'     => 'active',
]);

// Going active for the first time links a real account when the e-mail matches.
$linkedTo = $response['data']['linked_user_id'];

Resending the Invite

post/api/v1/admin/clients/{id}/subusers/{subuser_id}/resend-invite
Clients/ResendClientSubuserInvite admin pending only

Sends the invite again to a sub-user still waiting for one.

Body
No body is needed, send an empty one. The invite goes to the address already stored on the record and cannot be redirected here.
Response fields data — 2
resentboolWhether the invite went out again.
idintSub-user id.
Errors 3
not_found404No such client or sub-user.
not_pending422The sub-user is not awaiting an invite. A live or switched-off record cannot be invited.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/subusers/5/resend-invite' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/subusers/5/resend-invite', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/subusers/5/resend-invite');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->ResendClientSubuserInvite([
    'id'         => 42,
    'subuser_id' => 5,
]);

Deleting a Sub-user

delete/api/v1/admin/clients/{id}/subusers/{subuser_id}
Clients/DeleteClientSubuser admin

Deletes the sub-user. The real client account it was linked to is not affected.

Response fields data — 2
deletedboolWhether the delete succeeded.
idintId of the deleted sub-user.
Errors 2
not_found404No such client or sub-user.
insufficient_scope403The key lacks the required scope.
Request
curl -X DELETE 'https://panel.example.com/api/v1/admin/clients/42/subusers/5' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/subusers/5', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/subusers/5');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->DeleteClientSubuser([
    'id'         => 42,
    'subuser_id' => 5,
]);

Pitfalls

An unrecognised permission is dropped silently

The permission list is a closed set. Send a key that is not in it and you get no error; the key is thrown away and the record is stored with fewer permissions than you meant. Read the permissions field back from the write to see what was granted.

Going active can link an account

The first time a sub-user is set to active, a matching e-mail on an existing client account links that account. That means someone can sign in as themselves and step into another client's panel, so know whose e-mail it is before changing status.

The invite only goes to one still waiting

The resend endpoint only works on a record that is still pending. To invite a live sub-user you have to move the status back first.

War das hilfreich?

Vielen Dank für Ihre Rückmeldung!

Brauchen Sie weitere Hilfe?

Unser Support-Team ist rund um die Uhr für Sie da, wenn Sie oben nicht fündig werden.