Server Groups
The five endpoints that manage the server groups letting products provision into a pool.
Overview
A server group lets a product be provisioned into a pool rather than onto one server. The product points at the group, and the fill strategy decides which server a new service lands on.
The group's type is not a field of its own: it comes from the members. That is why only servers on the same type and the same module can go into one group; a mixed list is refused outright.
Reference
Listing the Groups
Returns the server groups.
hosting or server. It comes from the members; you do not set it.curl 'https://panel.example.com/api/v1/admin/products/server-groups' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/server-groups', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/server-groups');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Products()->GetServerGroups();{
"data": [
{
"id": 2,
"name": "Shared Pool",
"type": "hosting",
"fill_type": 1,
"server_ids": [34, 35],
"server_count": 2
}
],
"meta": { "total": 1, "page": 1, "limit": 25, "next_page": 0 }
}Group Detail
Returns one server group. The schema is the same as a list item.
hosting or server. It comes from the members; you do not set it.curl 'https://panel.example.com/api/v1/admin/products/server-groups/2' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/server-groups/2', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/server-groups/2');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Products()->GetServerGroup(['id' => 2]);Creating a Group
Opens a server group. Every member has to share the same type and the same module.
hosting or server. It comes from the members, so it is never part of the request.name was empty.curl -X POST 'https://panel.example.com/api/v1/admin/products/server-groups' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"Shared Pool","servers":[34,35],"fill_type":1}'const res = await fetch('https://panel.example.com/api/v1/admin/products/server-groups', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Shared Pool',
servers: [34, 35],
fill_type: 1,
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/server-groups');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Shared Pool',
'servers' => [34, 35],
'fill_type' => 1,
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Filter out type and module mismatches yourself: a mixed list is refused outright.
$servers = Api::Products()->GetServers([], ['limit' => 100])['data'];
$cpanel = array_column(
array_filter($servers, fn (array $s): bool => $s['type'] === 'cPanel'),
'id',
);
Api::Products()->CreateServerGroup([
'name' => 'Shared Pool',
'servers' => $cpanel,
]);Updating a Group
Applies the fields you send. Send the member list and the group becomes exactly that list.
hosting or server. It comes from the members; you do not set it.curl -X PATCH 'https://panel.example.com/api/v1/admin/products/server-groups/2' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"servers":[34,35,36]}'const res = await fetch('https://panel.example.com/api/v1/admin/products/server-groups/2', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ servers: [34, 35, 36] }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/server-groups/2');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['servers' => [34, 35, 36]]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// To ADD a server send the current list too, or the others leave the group.
$group = Api::Products()->GetServerGroup(['id' => 2])['data'];
Api::Products()->UpdateServerGroup([
'id' => 2,
'servers' => [...$group['server_ids'], 36],
]);Deleting a Group
Deletes the server group. The delete is refused while a product still points at it.
gate:product.server_group_delete hook vetoed the operation.curl -X DELETE 'https://panel.example.com/api/v1/admin/products/server-groups/2' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/server-groups/2', {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/server-groups/2');
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);// Deleting the group does not delete its members: the servers stay, only the grouping goes.
$response = Api::Products()->DeleteServerGroup(['id' => 2]);Pitfalls
Because the group takes its type from its members, servers on different types or different modules cannot sit together. Send such a list and none of them is added; the request comes back as mixed_servers. Filter the list on your side first.
Sending a server list on an update makes the group exactly that list; servers missing from it leave. To add one server, read the current list first, append to it, and send all of it back.
A server with no account capacity cannot be a member, and the request comes back as no_capacity. The fill strategy decides by capacity, so this is not a formality but a working condition.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.