Product Categories
The six endpoints that read, open, edit and delete product categories inside a group and manage their images.
Overview
A category is the container that gathers products inside a group. The flat list of categories comes from the lookup in the product endpoints article. The six here read, open, edit and delete one category, and manage its images.
Categories and special groups live in the same table and return the same schema. Read is_category to see which one you have.
Reference
Category Detail
Returns one category with all of its settings and language content.
active ya da inactive.visible ya da invisible.font ya da image.curl 'https://panel.example.com/api/v1/admin/products/categories/18' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/categories/18', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/categories/18');
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()->GetProductCategory(['id' => 18]);
// is_category tells you whether the row is a category or a top-level group.
$isCategory = $response['data']['is_category'];Creating a Category
Opens a category inside a group. A single field decides which group it lands in.
hosting, server, software, or the id-suffixed key for a special group. It comes from the groups lookup.active ya da inactive.font ya da image.201. The same schema as the category detail above.group was empty.curl -X POST 'https://panel.example.com/api/v1/admin/products/categories' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"group":"hosting","title":{"en":"Reseller Hosting"},"rank":2}'const res = await fetch('https://panel.example.com/api/v1/admin/products/categories', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
group: 'hosting',
title: { en: 'Reseller Hosting' },
rank: 2,
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/categories');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'group' => 'hosting',
'title' => ['en' => 'Reseller Hosting'],
'rank' => 2,
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// For a special group the key carries the id as a suffix; read it from the groups lookup.
$response = Api::Products()->CreateProductCategory([
'group' => 'special-5',
'title' => ['en' => 'Wildcard Certificates'],
]);Updating a Category
Applies the fields you send. The group a category belongs to cannot be changed here.
active ya da inactive.font ya da image.200. The same schema as the category detail above.curl -X PATCH 'https://panel.example.com/api/v1/admin/products/categories/18' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"status":"inactive","rank":5}'const res = await fetch('https://panel.example.com/api/v1/admin/products/categories/18', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: 'inactive', rank: 5 }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/categories/18');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['status' => 'inactive', 'rank' => 5]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Products()->UpdateProductCategory([
'id' => 18,
'status' => 'inactive',
'rank' => 5,
]);Deleting a Category
Deletes the category.
gate:product.group_delete hook vetoed the operation.curl -X DELETE 'https://panel.example.com/api/v1/admin/products/categories/18' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/categories/18', {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/categories/18');
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);$response = Api::Products()->DeleteProductCategory(['id' => 18]);Uploading a Category Image
Uploads the category's icon or its header background.
icon or header-background. Defaults to icon.icon or header-background.curl -X POST 'https://panel.example.com/api/v1/admin/products/categories/18/image' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"image":"https://cdn.example.com/icon.png","type":"icon"}'const res = await fetch('https://panel.example.com/api/v1/admin/products/categories/18/image', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
image: 'https://cdn.example.com/icon.png',
type: 'icon',
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/categories/18/image');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'image' => 'https://cdn.example.com/icon.png',
'type' => 'icon',
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Uploading an icon image also sets the icon type to image; the font icon no longer applies.
$response = Api::Products()->UploadCategoryImage([
'id' => 18,
'image' => 'https://cdn.example.com/icon.png',
'type' => 'icon',
]);Deleting a Category Image
Removes the category's icon or its header background.
icon or header-background. Defaults to icon.curl -X DELETE -G 'https://panel.example.com/api/v1/admin/products/categories/18/image' \
-H "Authorization: Bearer $API_KEY" \
-d type=header-backgroundconst url = new URL('https://panel.example.com/api/v1/admin/products/categories/18/image');
url.searchParams.set('type', 'header-background');
const res = await fetch(url, {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$url = 'https://panel.example.com/api/v1/admin/products/categories/18/image?' . http_build_query(['type' => 'header-background']);
$ch = curl_init($url);
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);$response = Api::Products()->DeleteProductCategoryImage(['id' => 18], [
'type' => 'header-background',
]);Pitfalls
The group field sets which group a category belongs to, and it works at creation only. It cannot be changed later: the update body has no such field. Moving a category to another group means opening a new one and deleting the old.
A clashing slug is refused on both create and update. The clash is looked for across records, not among sibling categories alone. A same-named category in another group stops you too, so a slug built from a title can collide where you did not expect it.
Uploading into the icon slot switches the icon type to image. The font icon you set before is stored but not shown. Going back to it means deleting the image and writing the icon type again.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.