Help Categories

8 Aufrufe Markdown

The five endpoints that open, read, update and remove help categories.

Overview

Categories group the help articles and build a tree. Every category can hang from another, and the ones at the root carry a parent of zero.

They follow the same contract as the articles. A title is wanted in every live language and a slug is optional, unique per language. An update writes the whole record.

One thing differs: a category's search engine fields are kept while indexing is on. Fields filled in while it is off are never saved.

Reference

Listing the Categories

get/api/v1/admin/knowledgebase/categories
Knowledgebase/GetKnowledgebaseCategories admin

Returns the help categories.

Query 3
searchstringSearches the title.
pageintWhich page.
limitintRecords per page. 100 at the most.
Response fields data[] — 5 + meta — 3
idintThe category id.
titlestringThe title. In the panel's current language.
routestringThe address slug.
statusstringWhether the category is on.
iconstringThe icon class.
totalintHow many categories there are. It comes back under meta.
pageintThe page you are on.
limitintThe page size.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/knowledgebase/categories' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/knowledgebase/categories', {
  headers: { Authorization: `Bearer ${apiKey}` },
});

const { data } = await res.json();
const byId = Object.fromEntries(data.map((c) => [c.id, c.title]));
$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/categories');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The listing holds NO parent id: building the tree wants the detail of every category.
$rows = Api::Knowledgebase()->GetKnowledgebaseCategories()['data'];
foreach ($rows as $c)
    $parent[$c['id']] = Api::Knowledgebase()->GetKnowledgebaseCategory(['cid' => $c['id']])['data']['parent_id'];

Opening a Category

post/api/v1/admin/knowledgebase/categories
Knowledgebase/CreateKnowledgebaseCategory admin every language needed

Opens a new help category.

Body 6
translationsobjectreqThe content per language. A title is needed in every live language.
titlestringThe category title. It is needed in every live language.
routestringThe address slug. Left empty it comes from the title.
sub_titlestringThe subtitle.
contentstringThe category description.
seo_titlestringThe search engine title. The search engine fields are kept while indexing is on.
seo_keywordsstringThe search engine keywords.
seo_descriptionstringThe search engine description.
parent_idintThe parent category. Zero means the root.
statusstringWhether the category is on.
rankintWhere it sits in the listing.
iconstringThe icon class.
seo_indexboolWhether the search engine fields are kept and indexed.
Response fields 201 — data — 7
dataobjectThe category made. Same shape as the read endpoint, and the new id comes under meta as well.
Errors 5
title_required422The title is missing in one of the live languages.
route_in_use422The slug is taken by another category in that language.
invalid_parent422A category cannot be its own parent.
vetoed422A hook refused the save.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/knowledgebase/categories' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"parent_id":0,"icon":"bi bi-book","translations":{"en":{"title":"Billing"}}}'
const res = await fetch('https://panel.example.com/api/v1/admin/knowledgebase/categories', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    parent_id: 0,
    icon: 'bi bi-book',
    seo_index: true,
    translations: { en: { title: 'Billing', sub_title: 'Invoices and payments' } },
  }),
});

const { data, meta } = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/categories');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'parent_id' => 0, 'translations' => ['en' => ['title' => 'Billing']],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The search engine fields are not kept while indexing is OFF: filling them in loses them.
Api::Knowledgebase()->CreateKnowledgebaseCategory([
    'seo_index' => true,
    'translations' => ['en' => ['title' => 'Billing', 'seo_title' => 'Billing help']],
]);

Reading a Category

get/api/v1/admin/knowledgebase/categories/{cid}
Knowledgebase/GetKnowledgebaseCategory admin

Returns a category with all of its languages.

Response fields data — 7
idintThe category id.
parent_idintThe parent category.
statusstringWhether the category is on.
rankintWhere it sits in the listing.
iconstringThe icon class.
seo_indexboolWhether search engine indexing is on.
translationsobjectThe content per language.
titlestringThe category title. It is needed in every live language.
routestringThe address slug. Left empty it comes from the title.
sub_titlestringThe subtitle.
contentstringThe category description.
seo_titlestringThe search engine title. The search engine fields are kept while indexing is on.
seo_keywordsstringThe search engine keywords.
seo_descriptionstringThe search engine description.
Errors 2
not_found404No such category.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/knowledgebase/categories/3' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch(`https://panel.example.com/api/v1/admin/knowledgebase/categories/${cid}`, {
  headers: { Authorization: `Bearer ${apiKey}` },
});

const { data } = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/categories/' . $cid);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The parent id comes HERE alone; call this for every category while building the tree.
$c = Api::Knowledgebase()->GetKnowledgebaseCategory(['cid' => $cid])['data'];
$isRoot = (int) $c['parent_id'] === 0;

Updating a Category

patch/api/v1/admin/knowledgebase/categories/{cid}
Knowledgebase/UpdateKnowledgebaseCategory admin a full write

Rewrites the category in full.

Body 6
translationsobjectreqThe content per language. The set is rewritten and a language you leave out is lost.
titlestringThe category title. It is needed in every live language.
routestringThe address slug. Left empty it comes from the title.
sub_titlestringThe subtitle.
contentstringThe category description.
seo_titlestringThe search engine title. The search engine fields are kept while indexing is on.
seo_keywordsstringThe search engine keywords.
seo_descriptionstringThe search engine description.
parent_idintThe parent category. Zero means the root.
statusstringWhether the category is on.
rankintWhere it sits in the listing.
iconstringThe icon class.
seo_indexboolWhether the search engine fields are kept and indexed.
Response fields data — 7
dataobjectThe category as it now stands. Same shape as the read endpoint.
Errors 6
not_found404No such category.
title_required422The title is missing in one of the live languages.
route_in_use422The slug is taken by another category in that language.
invalid_parent422A category cannot be its own parent.
vetoed422A hook refused the save.
insufficient_scope403The key lacks the required scope.
Request
curl -X PATCH 'https://panel.example.com/api/v1/admin/knowledgebase/categories/3' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"status":"inactive","translations":{"en":{"title":"Billing"}}}'
const cur = await fetch(`https://panel.example.com/api/v1/admin/knowledgebase/categories/${cid}`, {
  headers: { Authorization: `Bearer ${apiKey}` },
}).then((r) => r.json());

const res = await fetch(`https://panel.example.com/api/v1/admin/knowledgebase/categories/${cid}`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ ...cur.data, rank: 2 }),
});
$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/categories/' . $cid);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($category),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Closing a category DOES NOT remove its articles, and a client can no longer reach them through it.
$c = Api::Knowledgebase()->GetKnowledgebaseCategory(['cid' => $cid])['data'];
$c['status'] = 'inactive';

Api::Knowledgebase()->UpdateKnowledgebaseCategory($c + ['cid' => $cid]);

Removing a Category

delete/api/v1/admin/knowledgebase/categories/{cid}
Knowledgebase/DeleteKnowledgebaseCategory admin

Removes a category and every translation of it.

Response fields data — 2
deletedboolWhether the delete ran.
idintThe id of the category removed.
Errors 3
not_found404No such category.
vetoed422A hook refused the delete.
insufficient_scope403The key lacks the required scope.
Request
curl -X DELETE 'https://panel.example.com/api/v1/admin/knowledgebase/categories/3' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch(`https://panel.example.com/api/v1/admin/knowledgebase/categories/${cid}`, {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/categories/' . $cid);
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);
// The articles inside STAY and fall out of any category; move them elsewhere first.
$arts = Api::Knowledgebase()->GetKnowledgebaseArticles()['data'];
foreach ($arts as $a) { /* move the ones whose category is $cid */ }

Api::Knowledgebase()->DeleteKnowledgebaseCategory(['cid' => $cid]);

Pitfalls

The listing is not enough to build the tree

The category listing does not carry the parent id; it gives the id, title, slug, state and icon alone. Building the tree wants each category read on its own, which on a large knowledge base means one call per category.

The update rewrites the translation set

As with the articles the update body is the whole category. Read the current state first and write over it, even to change the order alone. The languages and fields you leave out go empty.

The search engine fields are not saved while indexing is off

The search engine title, keywords and description stay only while indexing is on. Filling them in while it is off raises no error and the values are not saved, showing empty on the next read. Turn indexing on first.

Removing a category orphans its articles

Removing a category does not remove the articles inside. They fall out of any category, and a client cannot reach them from the category listing. The articles are still found by address and by search while the navigation breaks. Move them first.

The self-parent check looks one step deep

A category cannot be its own parent and that is checked. Longer loops carry no such guard: A under B with B under A passes. Take care not to build a cycle while rearranging the tree.

War das hilfreich?

Vielen Dank für Ihre Rückmeldung!

Brauchen Sie weitere Hilfe?

Unser Support-Team ist rund um die Uhr für Sie da, wenn Sie oben nicht fündig werden.