Client Security

24 views Markdown

The five endpoints that manage the password, verification, two-step sign-in and the security rules set on a client.

Overview

These five endpoints carry the security decisions an operator applies to a client account: setting the password, marking a field verified, turning on two-step verification, tightening sign-in and payment rules, and limiting support access.

None of them ask the client anything. They all apply directly, which is why a key carrying these endpoints is worth keeping narrow.

Reference

Changing the Password

put/api/v1/admin/clients/{id}/password
Clients/SetClientPassword admin no old password asked

Sets the client's password directly. The current one is not asked for.

Body 2
passwordstringrequiredThe new password. At least options/password-length characters; defaults to 6.
password_confirmationstringIf sent, it must match password exactly. Leave it out and no check is made.
Response fields data — 1
changedboolWhether the password was changed.
Errors 5
not_found404No such client.
password_required422The password is empty.
password_too_short422The password is below the minimum length. The limit comes back in error.details.min.
password_mismatch422The confirmation does not match the password.
insufficient_scope403The key lacks the required scope.
Request
curl -X PUT 'https://panel.example.com/api/v1/admin/clients/42/password' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"password":"Str0ngP@ssw0rd","password_confirmation":"Str0ngP@ssw0rd"}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/password', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    password: 'Str0ngP@ssw0rd',
    password_confirmation: 'Str0ngP@ssw0rd',
  }),
});

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

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->SetClientPassword([
    'id'       => 42,
    'password' => $newPassword,
]);

Verifying Email and Phone

post/api/v1/admin/clients/{id}/verify
Clients/VerifyClient admin

Marks a field as verified. No code is sent to the client; the flag is set directly.

Body 1
typestringrequiredemail or phone.
Response fields data — 2
verifiedboolWhether the verification was applied.
typestringThe field that was verified: email or phone.
Errors 3
not_found404No such client.
type_invalid422type is neither of the two values.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/verify' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"type":"email"}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/verify', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ type: 'email' }),
});

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

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// You set the flag here after running your own verification flow.
$response = Api::Clients()->VerifyClient([
    'id'   => 42,
    'type' => 'email',
]);

Two-Step Verification

put/api/v1/admin/clients/{id}/2fa
Clients/SetClient2fa admin

Turns the client's two-step verification on or off.

Body 2
enabledboolrequiredTurns it on or off.
methodstringName of the Authentication module to use, for example GoogleAuthenticator. Required only when enabling; not needed to disable.
Response fields data — 2
enabledboolThe two-step state as it now stands.
methodstringThe module that was selected. Comes back only when enabling.
Errors 3
not_found404No such client.
method_required422method was missing on an enable request.
insufficient_scope403The key lacks the required scope.
Request
curl -X PUT 'https://panel.example.com/api/v1/admin/clients/42/2fa' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"enabled":true,"method":"GoogleAuthenticator"}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/2fa', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ enabled: true, method: 'GoogleAuthenticator' }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/2fa');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'enabled' => true,
        'method'  => 'GoogleAuthenticator',
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// You do not need to send a method when turning it off.
$response = Api::Clients()->SetClient2fa([
    'id'      => 42,
    'enabled' => false,
]);

Security Settings

patch/api/v1/admin/clients/{id}/security-settings
Clients/UpdateClientSecuritySettings admin

Changes the security rules that apply to this client. Only the fields you send change.

Body 6
block_proxyboolBlocks sign-in from behind a proxy.
allow_proxyboolExempts this client from the proxy check.
require_birthdayboolMakes the date of birth required.
require_adult_ageboolRequires an over-18 check.
force_document_filtersint[]Ids of the document verification filters to enforce. List: clients/document-filters.
blocked_gatewaysstring[]Keys of the payment methods closed to this client.
Response fields data — 6
block_proxyboolWhether sign-in from behind a proxy is blocked. The whole state comes back, not only the part you sent.
allow_proxyboolWhether the client is exempt from the proxy check.
require_birthdayboolWhether the date of birth is required.
require_adult_ageboolWhether the over-18 check is on.
force_document_filtersint[]Ids of the enforced document verification filters.
blocked_gatewaysstring[]Keys of the payment methods closed to this client.
Errors 2
not_found404No such client.
insufficient_scope403The key lacks the required scope.
Request
curl -X PATCH 'https://panel.example.com/api/v1/admin/clients/42/security-settings' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"block_proxy":true,"blocked_gateways":["PayPal"]}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/security-settings', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ block_proxy: true, blocked_gateways: ['PayPal'] }),
});

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

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

Support Settings

patch/api/v1/admin/clients/{id}/support-settings
Clients/UpdateClientSupportSettings admin

Restricts or closes the client's ability to open support tickets.

Body 2
ticket_restrictedboolRestricts opening tickets.
ticket_blockedboolBlocks opening tickets outright.
Response fields data — 2
ticket_restrictedboolWhether opening tickets is restricted. The whole state comes back, not only the part you sent.
ticket_blockedboolWhether opening tickets is blocked.
Errors 2
not_found404No such client.
insufficient_scope403The key lacks the required scope.
Request
curl -X PATCH 'https://panel.example.com/api/v1/admin/clients/42/support-settings' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"ticket_restricted":true}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/support-settings', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ ticket_restricted: true }),
});

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

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

Pitfalls

Changing the password asks for no old one

The endpoint is an operator tool: it writes a new password without knowing the current one. The flow where a client changes their own password is a different one and lives in the Client API.

Verifying sends no code

verify only sets the flag; no email or SMS reaches the client. Call it after your own verification flow has run, not instead of it.

The two proxy fields are opposites

block_proxy blocks and allow_proxy exempts. Sending both as on leaves a contradictory state; send only the one you mean.

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.