Reaching Into a Service

4 views Markdown

The seven endpoints reaching the panel behind a service.

Overview

Behind a service there is usually a panel: a hosting control panel, a virtualisation interface or a software installation. These seven endpoints reach it.

They are all live calls: WISECP returns the panel's state at that moment rather than its own records. A slow panel makes a slow answer, and a panel that is down makes the endpoint fail.

What is opened to the customer is kept narrow next to the admin side. Creating, suspending and terminating are never reachable here.

Reference

Reading the Panel Overview

get/api/v1/client/services/{id}/dashboard
Services/GetServiceDashboard it asks the module live

Returns the overview and the abilities of the panel behind the service.

Response fields data — 8
modulestringThe panel module name.
panel_namestringThe panel name shown.
sso_availableboolWhether single sign-on works. It closes while access is restricted.
can_change_passwordboolWhether the panel password can be changed here.
has_toolsboolWhether the module offers a tool surface.
methodsarrayThe module methods the customer may call. The method endpoint takes these alone.
quick_actionsarrayThe shortcuts on the overview. Each carries a key, a label and whether it runs an action.
overviewobjectThe state the panel reports.
gaugesarrayThe limited resources. Each carries what is used, the total and the unit, and a total at zero or below means no limit.
resourcesarrayThe counters and values the module reports.
accountarrayThe live panel account details. The sign-in details drop while access is restricted.
Errors 4
not_found404No such service, it is not yours, it has no module, or it is not live.
not_actionable422The service is not in a live state.
module_unavailable422The module could not be set up. The panel cannot be reached now.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/client/services/622/dashboard' \
  -H "Authorization: Bearer $CLIENT_KEY"
const res = await fetch(`https://panel.example.com/api/v1/client/services/${id}/dashboard`, {
  headers: { Authorization: `Bearer ${clientKey}` },
});

const { data } = await res.json();
if (data.sso_available) showOpenPanelButton();
$ch = curl_init('https://panel.example.com/api/v1/client/services/' . $id . '/dashboard');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $clientKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// This call goes LIVE TO THE PANEL: a slow panel makes a slow answer, so cache it on screen.
$d = Kernel::internal('client:Services/GetServiceDashboard',
    ['owner_id' => $uid, 'id' => $id])['data'];
$can = $d['methods'];

Listing the Tools

get/api/v1/client/services/{id}/tools
Services/GetServiceTools it asks the module live

Returns the panel tools the service offers.

Response fields data[] — 4
keystringThe tool key. This is what the data and action endpoints take.
groupstringThe panel's grouping.
labelstringThe name shown.
capabilitiesarrayThe actions the tool supports. Creating, editing, removing and the like.
Errors 3
not_found404No such service, it is not yours, it has no module, or it is not live.
tools_not_supported422The module has no tool surface.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/client/services/622/tools' \
  -H "Authorization: Bearer $CLIENT_KEY"
const res = await fetch(`https://panel.example.com/api/v1/client/services/${id}/tools`, {
  headers: { Authorization: `Bearer ${clientKey}` },
});

const { data } = await res.json();
const byGroup = Object.groupBy(data, (t) => t.group);
$ch = curl_init('https://panel.example.com/api/v1/client/services/' . $id . '/tools');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $clientKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The customer list is NARROWER than the admin one: the tools opened to the customer area come alone.
$tools = Kernel::internal('client:Services/GetServiceTools',
    ['owner_id' => $uid, 'id' => $id])['data'];
$keys = array_column($tools, 'key');

Reading a Tool's Data

get/api/v1/client/services/{id}/tools/{tool}
Services/GetServiceToolData it asks the module live

Pulls what a tool lists from the panel.

Query 1
actionstringThe read action. The tool's default listing stands in when left out.
Response fields data
dataobjectWhat the tool returns. Its shape follows the tool and the module, and there is no fixed contract.
Errors 5
not_found404The tool is unknown or the service was not found.
tools_not_supported422The module has no tool surface.
tool_rejected422A hook refused the call.
tool_data_failed500The call to the panel failed.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/client/services/622/tools/databases' \
  -H "Authorization: Bearer $CLIENT_KEY"
const res = await fetch(`https://panel.example.com/api/v1/client/services/${id}/tools/${tool}`, {
  headers: { Authorization: `Bearer ${clientKey}` },
});

const { data } = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/client/services/' . $id . '/tools/' . $tool);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $clientKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The shape belongs to THE MODULE and is no API contract: the fields move when the server panel does.
$rows = Kernel::internal('client:Services/GetServiceToolData',
    ['owner_id' => $uid, 'id' => $id, 'tool' => $tool])['data'];

Running a Tool Action

post/api/v1/client/services/{id}/tools/{tool}/{action}
Services/RunServiceToolAction it changes the server

Runs a create, an edit or a removal on a tool.

Body
*objectA free body belonging to the tool. Its fields are what the module expects.
Response fields data
dataobjectWhat the module gave back.
Errors 5
not_found404No such service, it is not yours, it has no module, or it is not live.
tools_not_supported422The module has no tool surface.
tool_rejected422A hook refused the call.
tool_action_failed500The call to the panel threw an error.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/client/services/622/tools/databases/create' \
  -H "Authorization: Bearer $CLIENT_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name":"shop"}'
const res = await fetch(`https://panel.example.com/api/v1/client/services/${id}/tools/${tool}/${action}`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${clientKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(payload),
});

const { data } = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/client/services/' . $id . '/tools/' . $tool . '/' . $action);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $clientKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// This call makes a real change ON THE SERVER and cannot be undone: put a removal behind a confirmation.
$out = Kernel::internal('client:Services/RunServiceToolAction',
    ['owner_id' => $uid, 'id' => $id, 'tool' => $tool, 'action' => 'create'] + $payload);

Running a Module Method

post/api/v1/client/services/{id}/module-method
Services/UseServiceModuleMethod declared methods only

Runs a method the module opened to the customer.

Body 1
methodstringreqThe method to run. It comes from the method list in the panel overview.
Response fields data — 4
methodstringThe method that ran.
redirect_urlstringThe address the module produced. It fills on a download or a redirect flow.
resultobjectWhat the method returned.
outputstringThe output caught where the method printed rather than returned.
Errors 6
not_found404No such service, it is not yours, it has no module, or it is not live.
method_required422No method name was sent.
invalid_method422The method was never declared open to the customer.
module_unavailable422The module could not be set up.
tool_rejected422A hook refused the call.
module_method_failed500The module method threw an error.
Request
curl -X POST 'https://panel.example.com/api/v1/client/services/622/module-method' \
  -H "Authorization: Bearer $CLIENT_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"method":"backup_download"}'
const res = await fetch(`https://panel.example.com/api/v1/client/services/${id}/module-method`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${clientKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ method }),
});

const { data } = await res.json();
if (data.redirect_url) window.location = data.redirect_url;
$ch = curl_init('https://panel.example.com/api/v1/client/services/' . $id . '/module-method');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $clientKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['method' => $method]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// DECLARED methods alone can be called: creating, suspending and terminating are never reachable here.
$d = Kernel::internal('client:Services/GetServiceDashboard',
    ['owner_id' => $uid, 'id' => $id])['data'];

if (in_array($method, $d['methods'], true))
    Kernel::internal('client:Services/UseServiceModuleMethod',
        ['owner_id' => $uid, 'id' => $id, 'method' => $method]);

Signing Into the Panel

post/api/v1/client/services/{id}/sso
Services/GetServiceSso a short-lived address

Makes a one-time address for signing into the panel without a password.

Body
No body is needed, so send an empty one. The panel account comes from the service record.
Response fields data — 1
urlstringThe address opening the panel. It is short-lived and not meant to be kept.
Errors 5
not_found404The service was not found, has no module, is not live, or access is restricted.
sso_not_supported422The module offers no single sign-on.
tool_rejected422A hook refused the call.
sso_failed500The module returned no address.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/client/services/622/sso' \
  -H "Authorization: Bearer $CLIENT_KEY"
const res = await fetch(`https://panel.example.com/api/v1/client/services/${id}/sso`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${clientKey}` },
});

const { data } = await res.json();
window.open(data.url, '_blank');
$ch = curl_init('https://panel.example.com/api/v1/client/services/' . $id . '/sso');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $clientKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The address is SHORT-LIVED and single use: never cache it and make a new one on each open.
$sso = Kernel::internal('client:Services/GetServiceSso',
    ['owner_id' => $uid, 'id' => $id])['data'];
header('Location: ' . $sso['url']);

Changing the Panel Password

post/api/v1/client/services/{id}/password
Services/ChangeServicePassword hosting and servers

Changes the panel password of the service.

Body 1
passwordstringreqThe new panel password. It has to meet the operator's least length.
Response fields data
dataobjectHow the change went.
Errors 5
not_found404No such service, it is not yours, it has no module, or it is not live.
password_too_short422The password is under the least length. That least value comes in the answer's detail.
password_not_supported422The service type does not suit, access is restricted, or the module lacks the ability.
password_change_failed422The panel refused the change.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/client/services/622/password' \
  -H "Authorization: Bearer $CLIENT_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"password":"a-long-new-secret"}'
const res = await fetch(`https://panel.example.com/api/v1/client/services/${id}/password`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${clientKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ password }),
});

if (res.status === 422) showRule(await res.json());
$ch = curl_init('https://panel.example.com/api/v1/client/services/' . $id . '/password');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $clientKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['password' => $new]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The panel password is not THE ACCOUNT password: a change here never touches the customer's sign-in.
$d = Kernel::internal('client:Services/GetServiceDashboard',
    ['owner_id' => $uid, 'id' => $id])['data'];

if ($d['can_change_password'])
    Kernel::internal('client:Services/ChangeServicePassword',
        ['owner_id' => $uid, 'id' => $id, 'password' => $new]);

Pitfalls

These endpoints go live to the panel

The overview, the tools and the tool data come from the panel itself rather than the WISECP database. A slow panel makes a long answer and an unreachable one makes the endpoint fail. Do not call these once per row on a listing screen; call them when the user truly asks.

Only declared methods can be called

The method endpoint takes the methods a module declared open to the customer. The breadth of the admin side is absent: creating, suspending and terminating are never reachable here. Read the list from the panel overview.

A tool's data shape is no contract

What a tool endpoint returns is the module's own output and can move when the server panel does. An interface bound tightly to the field names breaks on a module update. Read the capabilities from the tool listing and build from those.

Restricting access closes two endpoints at once

Where the operator turns access restriction on for a product, both single sign-on and the panel password change close, and the sign-in details drop from the overview. The tools keep working. Read the two flags in the overview rather than treating it as a fault.

The sign-on address is not for keeping

The single sign-on address is short-lived and single use. Keeping it somewhere and opening it later does not work, and while it lives it carries the right to sign in. Make a new one on each open and write it to no log.

The panel password is not the account password

The password changed here belongs to the panel on the server and the customer's WISECP sign-in is untouched. The ability exists on hosting and server services alone, and the module has to support it.

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.