Website Menus
The seven endpoints that build, order and edit the trees of five menu groups.
Overview
Website menus live in five separate groups: the header, the footer, the client panel, mobile and the sidebar. Each group is its own tree, and items do not move between groups.
An item either points at a page or carries a link written by hand. One pointing at a page follows that page when its address changes, while a hand-written link stays as it is.
The title, description, badge and mega-menu content are kept per language. The order and the nesting are written through an endpoint of their own, in one call.
Reference
Reading the Menu Tree
Returns a whole menu group as a nested tree.
header, footer, clientArea, mobile, sidebar. The header menu by default.curl 'https://panel.example.com/api/v1/admin/website/menus?group=header' \
-H "Authorization: Bearer $API_KEY"const url = new URL('https://panel.example.com/api/v1/admin/website/menus');
url.searchParams.set('group', 'header');
const res = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/menus?' . http_build_query(['group' => 'header']));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Each group is a SEPARATE tree; seeing them all takes five calls.
$header = Api::Website()->GetMenus([], ['group' => 'header'])['data'];
$footer = Api::Website()->GetMenus([], ['group' => 'footer'])['data'];Reading the Page Options
Returns the pages a menu item can point at.
curl 'https://panel.example.com/api/v1/admin/website/menus/page-options' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/website/menus/page-options', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/menus/page-options');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Do not write the keys into your code: the list follows the installation's own pages.
$options = Api::Website()->GetMenuPageOptions()['data'];Reordering the Menu
Writes the order and the nesting of menu items in one call.
curl -X PUT 'https://panel.example.com/api/v1/admin/website/menus/reorder' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"orders":[{"id":10,"position":0,"parentId":0,"submenuOrder":[{"id":11,"position":0,"parentId":10}]}]}'const res = await fetch('https://panel.example.com/api/v1/admin/website/menus/reorder', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
orders: [
{
id: 10,
position: 0,
parentId: 0,
submenuOrder: [{ id: 11, position: 0, parentId: 10 }],
},
],
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/menus/reorder');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'orders' => [[
'id' => 10,
'position' => 0,
'parentId' => 0,
'submenuOrder' => [['id' => 11, 'position' => 0, 'parentId' => 10]],
]],
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// An item you leave out STAYS PUT: a partial list can leave the tree half-moved.
$r = Api::Website()->ReorderMenus(['orders' => $tree]);
$written = $r['data']['reordered'];Reading One Menu Item
Returns one menu item with its languages.
curl 'https://panel.example.com/api/v1/admin/website/menus/10' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/website/menus/${id}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/menus/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The children list comes back EMPTY here; use the group endpoint to see the tree.
$item = Api::Website()->GetMenu(['id' => $id])['data'];Adding a Menu Item
Adds a new item to a menu.
header, footer, clientArea, mobile, sidebar.curl -X POST 'https://panel.example.com/api/v1/admin/website/menus' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"group":"header","page":"home","languages":{"tr":{"title":"Ana Sayfa"}}}'const res = await fetch('https://panel.example.com/api/v1/admin/website/menus', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
group: 'header',
page: 'home',
languages: {
en: { title: 'Home', description: 'Back to homepage', label: 'New' },
},
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/menus');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'group' => 'header',
'page' => 'home',
'languages' => ['en' => ['title' => 'Home']],
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The title is written PER LANGUAGE; a language you leave out shows an empty label.
Api::Website()->CreateMenu([
'group' => 'header',
'page' => 'home',
'languages' => ['en' => ['title' => 'Home'], 'tr' => ['title' => 'Ana Sayfa']],
]);Updating a Menu Item
Changes the menu item fields you send.
curl -X PATCH 'https://panel.example.com/api/v1/admin/website/menus/10' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"rank":1,"languages":{"tr":{"title":"Anasayfa"}}}'const res = await fetch(`https://panel.example.com/api/v1/admin/website/menus/${id}`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
rank: 1,
languages: { en: { title: 'Homepage' } },
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/website/menus/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'rank' => 1,
'languages' => ['en' => ['title' => 'Homepage']],
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// An item cannot move to another GROUP; the group is given at creation alone.
Api::Website()->UpdateMenu(['id' => $id, 'parent' => $newParent]);Deleting a Menu Item
Removes a menu item and everything beneath it.
curl -X DELETE 'https://panel.example.com/api/v1/admin/website/menus/10' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/website/menus/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const { data } = await res.json();
console.log(data.removed); // [10, 11]$ch = curl_init('https://panel.example.com/api/v1/admin/website/menus/' . $id);
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 a parent takes the SUBMENU with it; move the children elsewhere first.
$gone = Api::Website()->DeleteMenu(['id' => $id])['data']['removed'];Pitfalls
Deleting a menu item also deletes everything beneath it. Removing a top-level heading takes every link under it out of the menu. The removed list in the response tells you what actually went, so move the children under another item first when you want to keep them.
The menu group is given at creation alone, and the update does not take it. Moving a header link into the footer means creating it again in the new group and removing the old one. The same holds for the reorder endpoint: it shifts things only within one tree.
The reorder endpoint writes only the items in your list, and whatever you leave out stays where it was. Sending part of the tree and skipping the rest can leave the menu half-moved. Read the number written from the response and weigh it against what you expected.
A menu title is written separately in each language. Skip one and the item shows there with an empty label: it does not vanish, it stands nameless. Switch a new language on and none of the existing items carries a title in it, so you have to walk through them all.
An item pointing at a page follows it by itself when the page address changes. A hand-written link stays as it is and breaks once the page moves. When the target is a page on your own site, use the page field instead of typing an address; the page options endpoint gives you the valid keys.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.