Help Articles
The five endpoints that write, read, update and remove help articles.
Overview
A help article lives in two layers. The structural data holds the category, the state, who sees it and the order. The content per language holds the title, slug, body, tags and the search engine fields.
A title is required in every live language. The slug is optional and comes from the title when left empty, and it has to be unique per language.
The update is named as a partial change and behaves as a full write. The translation set is saved again for every language. Read the current state before changing anything.
Reference
Listing the Articles
Returns the help articles with their read and vote counts.
curl 'https://panel.example.com/api/v1/admin/knowledgebase/articles?search=password' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/knowledgebase/articles', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const { data } = await res.json();
const poor = data.filter((a) => a.useless > a.useful);$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/articles');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The title comes in ONE language; comparing translations wants the detail endpoint.
$rows = Api::Knowledgebase()->GetKnowledgebaseArticles()['data'];
$weak = array_filter($rows, fn ($a) => $a['useless'] > $a['useful']);Writing an Article
Opens a new help article.
curl -X POST 'https://panel.example.com/api/v1/admin/knowledgebase/articles' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"category_id":3,"translations":{"en":{"title":"How to reset your password","content":"<p>Open the account page.</p>"}}}'const res = await fetch('https://panel.example.com/api/v1/admin/knowledgebase/articles', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
category_id: 3,
status: 'published',
translations: {
en: { title: 'How to reset your password', route: 'reset-password', content: html },
},
}),
});
const { data, meta } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/articles');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'category_id' => 3,
'translations' => ['en' => ['title' => $title, 'content' => $html]],
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// It wants a title in EVERY live language; a missing one turns the whole call down and nothing is saved.
$langs = array_column(Api::Reference()->GetLanguages()['data'], 'code');
foreach ($langs as $l) $t[$l] = ['title' => $titles[$l] ?? $titles['en']];
Api::Knowledgebase()->CreateKnowledgebaseArticle(['translations' => $t]);Reading an Article
Returns an article with all of its languages.
curl 'https://panel.example.com/api/v1/admin/knowledgebase/articles/12' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/knowledgebase/articles/${id}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const { data } = await res.json();
const missing = langs.filter((l) => ! data.translations[l]?.content);$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/articles/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Read here BEFORE updating: an update rewrites the WHOLE translation set.
$a = Api::Knowledgebase()->GetKnowledgebaseArticle(['id' => $id])['data'];
$a['translations']['en']['content'] = $newHtml;Updating an Article
Rewrites the article in full.
curl -X PATCH 'https://panel.example.com/api/v1/admin/knowledgebase/articles/12' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"status":"draft","translations":{"en":{"title":"How to reset your password","content":"<p>Updated steps.</p>"}}}'const cur = await fetch(`https://panel.example.com/api/v1/admin/knowledgebase/articles/${id}`, {
headers: { Authorization: `Bearer ${apiKey}` },
}).then((r) => r.json());
cur.data.translations.en.content = html;
const res = await fetch(`https://panel.example.com/api/v1/admin/knowledgebase/articles/${id}`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(cur.data),
});$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/articles/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($article),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// It is named PATCH and behaves as a FULL WRITE: the translations are saved again for every language.
$a = Api::Knowledgebase()->GetKnowledgebaseArticle(['id' => $id])['data'];
$a['translations']['en']['content'] = $newHtml;
Api::Knowledgebase()->UpdateKnowledgebaseArticle($a + ['id' => $id]);Removing an Article
Removes an article, every translation of it and its header image.
curl -X DELETE 'https://panel.example.com/api/v1/admin/knowledgebase/articles/12' \
-H "Authorization: Bearer $API_KEY"const res = await fetch(`https://panel.example.com/api/v1/admin/knowledgebase/articles/${id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/knowledgebase/articles/' . $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);// The slug goes as well: old links to that address land on a not-found page.
Api::Knowledgebase()->UpdateKnowledgebaseArticle(['id' => $id, 'status' => 'draft'] + $article);
// moving it to a draft rather than removing keeps the addressPitfalls
The update body is the whole article. Sending one language alone leaves the others unsaved and they go. Take the full set from the read endpoint first, change it there, and send all of it back.
A title is needed in every live language, and one missing gives 422 while no language is saved. Articles that exist carry no title in a language added later, so the first edit meets this error. Take the language list from the reference endpoint and build the body from it.
Using one slug on two articles gives route_in_use. The check is per language, so a slug taken in English can be free in another. During a bulk import the clash appears in one language alone and stops the whole save.
The private field limits an article to signed-in clients and it still shows in the panel and in search. Move the state to a draft when you mean to take content out of sight entirely.
Removing an article takes the slug with it and outside links to that address land on a not-found page. Moving one that search engines know to a draft is often better. The content goes out of sight and the address stays.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.