Staff Accounts

10 views Markdown

The six endpoints that open, update and remove the staff accounts using the panel.

Overview

Staff accounts are the people who can sign into the panel. They live in a record set apart from clients, yet their e-mail addresses share one pool with them.

What an account can do rests on its privilege group, and which support tickets it sees on the departments it is assigned to. Both are handled at endpoints of their own.

The installation's founding account is guarded: it cannot be removed and its privileges cannot change. Your own account is guarded in part as well, since you can neither lower your own privileges nor remove yourself.

Reference

Listing the Staff

get/api/v1/admin/admins
Admins/GetStaff admin

Returns the accounts that can sign into the panel.

Query 3
pageintWhich page.
limitintRecords per page. Clamped between one and a hundred.
searchstringSearches the name, e-mail, phone and id.
Response fields data[] — 7 + meta — 4
idintThe staff id.
full_namestringTheir name.
emailstringTheir e-mail address.
statusstringWhether the account is live.
privilege_namestringThe name of the privilege group they belong to.
departmentsstring[]The departments they are assigned to.
is_rootboolWhether it is the installation's founding account. It cannot be removed and its privileges cannot change.
totalintHow many there are. It comes back under meta.
pageintThe page you are on.
limitintThe page size.
next_pageintThe next page. Zero means you are on the last one.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/admins' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/admins', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/admins');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The founding account carries its own mark; leave it out when writing bulk work.
$staff = Api::Admins()->GetStaff()['data'];
$rest  = array_filter($staff, fn ($s) => ! $s['is_root']);

Opening a Staff Account

post/api/v1/admin/admins
Admins/CreateStaff admin grants panel access

Opens a new account able to sign into the panel.

Body 15
full_namestringreqTheir name.
emailstringreqTheir e-mail address. Unique across staff and clients alike.
passwordstringreqThe account password.
password_confirmationstringreqThe password again.
privilegeintreqThe privilege group to join. This sets what the account can do in the panel.
langstringreqThe panel language.
display_namestringThe name clients see. Left empty, the real name shows.
phonestringThe phone number. Long enough, it gets formatted and checked for being unique.
departmentsint[]The departments to assign. Left out on an update, the current ones stay.
signatureobject | stringThe reply signature per language.
notesstringA note on the account.
display_modestringThe panel's look preference.
menu_statestringHow the menu opens.
statusstringWhether the account is live. Live by default.
avatarstringThe profile picture.
Response fields 201 — data
dataobjectThe account opened. Same shape as the detail endpoint.
Errors 4
staff_save_failed422The e-mail or phone is taken, the privilege group is not valid, or the passwords differ.
create_failed422The account could not be opened.
blocked_by_gate422A hook refused the account.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/admins' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"full_name":"Ayse Yilmaz","email":"[email protected]","password":"G1zliParola","password_confirmation":"G1zliParola","privilege":2,"lang":"tr"}'
const res = await fetch('https://panel.example.com/api/v1/admin/admins', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    full_name: 'John Doe',
    email: '[email protected]',
    password: secret,
    password_confirmation: secret,
    privilege: 2,
    lang: 'en',
    departments: [1, 2],
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/admins');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'full_name'             => 'John Doe',
        'email'                 => '[email protected]',
        'password'              => $secret,
        'password_confirmation' => $secret,
        'privilege'             => 2,
        'lang'                  => 'en',
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The privilege group decides WHAT THE ACCOUNT CAN DO; do not default to the widest one.
Api::Admins()->CreateStaff([
    'full_name'             => 'John Doe',
    'email'                 => $email,
    'password'              => $secret,
    'password_confirmation' => $secret,
    'privilege'             => $limitedGroupId,
    'lang'                  => 'en',
]);

Reading One Staff Member

get/api/v1/admin/admins/{id}
Admins/GetStaffMember admin

Returns one staff account with all of its fields.

Response fields data — 21
idintThe staff id.
statusstringWhether the account is live.
full_namestringTheir name.
namestringTheir first name.
surnamestringTheir last name.
emailstringTheir e-mail address.
phonestringTheir phone number.
langstringThe panel language.
privilege_idintThe privilege group they belong to.
gsm_ccstringTheir mobile country code.
gsmstringTheir mobile number.
residence_addressstringTheir address.
notesstringThe note on the account.
display_modestringTheir look preference.
menu_statestringHow their menu opens.
signatureobject | stringTheir reply signature per language.
has_2faboolWhether the second step of sign-in is on.
authentication_methodsstring[]The names of the verification methods in use. Only the names, and never a secret.
department_idsint[]The departments they are assigned to.
avatar_urlstringThe address of their profile picture.
is_rootboolWhether it is the founding account.
Errors 2
not_found404No such staff member.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/admins/5' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch(`https://panel.example.com/api/v1/admin/admins/${id}`, {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/admins/' . $id);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The password NEVER comes back, and on the verification side only method NAMES show.
$s = Api::Admins()->GetStaffMember(['id' => $id])['data'];

Updating a Staff Member

patch/api/v1/admin/admins/{id}
Admins/UpdateStaff admin some fields are guarded

Changes the staff account fields you send.

Body 15
full_namestringTheir name.
emailstringTheir e-mail address.
passwordstringA new password. Send it together with its repeat.
password_confirmationstringThe new password again.
privilegeintThe privilege group. Ignored on the founding account and on your own.
langstringThe panel language.
display_namestringThe name clients see. Left empty, the real name shows.
phonestringThe phone number. Long enough, it gets formatted and checked for being unique.
departmentsint[]The departments to assign. Left out on an update, the current ones stay.
signatureobject | stringThe reply signature per language.
notesstringA note on the account.
display_modestringThe panel's look preference.
menu_statestringHow the menu opens.
statusstringWhether the account is live. Live by default.
avatarstringThe profile picture.
Response fields data — 21
dataobjectThe account as it now stands. Same shape as the detail endpoint.
Errors 3
not_found404No such staff member.
staff_save_failed422The e-mail or phone is taken, the privilege group is not valid, or the passwords differ.
insufficient_scope403The key lacks the required scope.
Request
curl -X PATCH 'https://panel.example.com/api/v1/admin/admins/5' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"full_name":"Ayse Kaya","departments":[1,3]}'
const res = await fetch(`https://panel.example.com/api/v1/admin/admins/${id}`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ full_name: 'Jane Doe', departments: [1, 3] }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/admins/' . $id);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['full_name' => 'Jane Doe', 'departments' => [1, 3]]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// A department list REPLACES what was there; leave the field out to keep the current ones.
Api::Admins()->UpdateStaff(['id' => $id, 'full_name' => 'Jane Doe']);

Removing a Staff Member

delete/api/v1/admin/admins/{id}
Admins/DeleteStaff admin

Removes a staff account.

Response fields data — 2
deletedboolWhether the delete ran.
idintThe id of the account removed.
Errors 6
not_found404No such staff member.
root_protected422The founding account cannot go.
self_protected422You cannot remove your own account.
delete_failed422The account could not be removed.
blocked_by_gate422A hook refused the delete.
insufficient_scope403The key lacks the required scope.
Request
curl -X DELETE 'https://panel.example.com/api/v1/admin/admins/6' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch(`https://panel.example.com/api/v1/admin/admins/${id}`, {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/admins/' . $id);
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);
// For someone who left, switch the account OFF rather than delete: past work keeps its owner.
Api::Admins()->UpdateStaff(['id' => $id, 'status' => 'passive']);

Turning Off the Second Step

post/api/v1/admin/admins/{id}/disable-authentication
Admins/DisableStaffAuthentication admin the panel asks for a password

Takes away a staff member's second sign-in step.

Body 1
methodstringreqThe verification method to turn off. Its name comes from the list on the staff detail.
Response fields data — 2
disabledboolWhether it was turned off.
methodstringThe method turned off.
Errors 2
not_found404No such staff member.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/admins/5/disable-authentication' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"method":"GoogleAuthenticator"}'
const res = await fetch(`https://panel.example.com/api/v1/admin/admins/${id}/disable-authentication`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ method: 'GoogleAuthenticator' }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/admins/' . $id . '/disable-authentication');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['method' => 'GoogleAuthenticator']),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// In the panel this asks for the ADMIN PASSWORD; over the API the key's scope is the only gate.
$s = Api::Admins()->GetStaffMember(['id' => $id])['data'];
foreach ($s['authentication_methods'] as $m)
    Api::Admins()->DisableStaffAuthentication(['id' => $id, 'method' => $m]);

Pitfalls

The e-mail pool is shared with clients

A staff account's e-mail has to be unique not only among staff but among clients too. The same address cannot carry both a client and a staff account. On an installation that also registers its own team as clients, this surfaces as an unexpected clash error.

You cannot lower your own privileges

The update call quietly ignores the privilege and status fields on the founding account and on your own. You cannot lock yourself out by accident, yet neither can you assume the value you sent was applied. Read the privilege group in the response and weigh it against what you expected.

The department list replaces what was there

Sending a department list on an update replaces what was assigned. Sending only the one you meant to add drops the rest. To keep the current assignments leave the field out entirely, and to add one, read the detail first and merge.

Turning off the second step is easier here

Turning off a staff member's second sign-in step asks for the administrator password in the panel, while here the key's scope is the only gate. A key carrying it can strip an account's extra protection outright. Weigh this scope on its own when handing keys out.

Switch a departed member off rather than delete

Deleting a staff member takes the account away, while the work they did, the replies they wrote and the notes they left keep pointing at them in the records. Switching the account off stops the sign-in and leaves the history readable. The founding account and your own cannot be removed at all.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.