Account State and Bulk Actions
The six endpoints that block an account, suspend or cancel its services in bulk, and chase unpaid invoices.
Overview
These endpoints change the state of an account and of the services attached to it. That covers blocking, bulk suspension, bulk cancellation and invoice reminders.
Blocking comes in two shapes. For a single client the block endpoint also stores the reason; for a list, bulk does the same job without one. The panel walks bulk service actions one at a time to drive its progress bar. The API does the whole set in one call.
Reference
Blocking an Account
Blocks the account or lifts the block. The client is notified.
true blocks, false lifts the block.gate:user.block hook vetoed the operation.curl -X PUT 'https://panel.example.com/api/v1/admin/clients/42/block' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"blocked":true,"reason":"Payment dispute"}'const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/block', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ blocked: true, reason: 'Payment dispute' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/block');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'blocked' => true,
'reason' => 'Payment dispute',
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Clients()->SetClientBlock([
'id' => 42,
'blocked' => true,
'reason' => 'Payment dispute',
]);
// If a hook vetoes, the call returns an error and nothing is blocked.
if (isset($response['error'])) {
$code = $response['error']['code'];
}Applying a Bulk Action
Applies the same action to a list of clients. Only member accounts are processed; admins are skipped.
verify marks e-mail and phone as verified, reactivate puts the account back to active, block blocks it.ids was empty.curl -X POST 'https://panel.example.com/api/v1/admin/clients/bulk' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"action":"verify","ids":[80,81]}'const res = await fetch('https://panel.example.com/api/v1/admin/clients/bulk', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'verify', ids: [80, 81] }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/bulk');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'action' => 'verify',
'ids' => [80, 81],
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$sent = [80, 81, 3];
$response = Api::Clients()->BulkClientActions([
'action' => 'verify',
'ids' => $sent,
]);
// Compare the lists to see what was skipped (admin accounts are not processed).
$skipped = array_diff($sent, $response['data']['processed'] ?? []);{
"data": {
"action": "verify",
"processed": [80, 81]
}
}{
"error": {
"code": "action_invalid",
"message": "action must be verify/reactivate/block."
}
}Suspending Every Service
Suspends every active service the client has. The reason is written as Account bulk suspended.
{id, error}. An empty array means all of them went through.curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/services/suspend' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/services/suspend', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/services/suspend');
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()->SuspendClientServices(['id' => 42]);
// Partial success is possible: some go through, others come back in 'failed'.
foreach ($response['data']['failed'] ?? [] as $fail) {
$serviceId = $fail['id'];
$reason = $fail['error'];
}Lifting the Bulk Suspension
Puts the bulk-suspended services back. It does not touch services suspended for another reason.
{id, error}. An empty array means all of them went through.curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/services/unsuspend' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/services/unsuspend', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/services/unsuspend');
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()->UnsuspendClientServices(['id' => 42]);Cancelling Every Service
Cancels every service the client has that is not already cancelled or finished.
{id, error}. An empty array means all of them went through.gate:user.services_bulk_cancel hook vetoed the operation.curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/services/cancel' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/services/cancel', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/services/cancel');
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()->CancelClientServices(['id' => 42]);Reminding About Unpaid Invoices
Sends a reminder notice for every unpaid invoice the client has.
curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/remind-invoices' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/remind-invoices', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/remind-invoices');
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()->RemindClientInvoices(['id' => 42]);
// No unpaid invoices is an error here, not an empty result.
if (($response['error']['code'] ?? '') === 'no_unpaid_invoices') {
return;
}Pitfalls
The bulk service endpoints can return 200 while some services still failed; those come back in the failed array as {id, error}. A client that only reads the status code will miss it.
The unsuspend endpoint only reopens services that were stopped by the bulk suspension. A service suspended for overdue payment or by hand stays where it is, which stops one call from silently restarting it.
An account in your list that is not a member gets skipped without a word. It does not appear in processed, so compare the list you sent with the one you get back.
Bulk cancellation asks for the admin password in the panel. The API has no such second step: the key's scope is enough. Hand out a key carrying this scope knowing exactly who holds it.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.