Website Categories

8 Aufrufe Markdown

The seven endpoints that create, move and remove blog and reference categories.

Overview

Categories exist for two content types: blog posts and references. Plain pages, contracts and news carry no category, so they never appear at these endpoints.

Categories form a tree. Each record can have a parent, and zero puts it at the root. That information comes back only at the detail endpoint, while the list gives records in a flat order.

The shape matches that of pages: content is kept per language, the type is fixed at creation, and the address is unique within each language. Images are simpler here, as a category has one header image.

Reference

Listing the Categories

get/api/v1/admin/website/categories
Website/GetCategories admin

Returns the categories of the type you pick.

Query 4
typestringWhich type: articles, references. Blog categories by default.
searchstringSearches the titles.
pageintWhich page.
limitintRecords per page. A hundred at most.
Response fields data[] — 6 + meta — 5
idintThe category id.
typestringThe category type.
statusstringWhether the category is live.
titlestringIts title in the current language.
routestringIts address in the current language.
created_atstringWhen it was created.
totalintHow many there are. It comes back under meta.
pageintThe page you are on.
limitintThe page size.
type stringThe type filtered on.
next_pageintThe next page. Zero means you are on the last one.
Errors 2
invalid_type422The category type is not recognised.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/website/categories?type=articles' \
  -H "Authorization: Bearer $API_KEY"
const url = new URL('https://panel.example.com/api/v1/admin/website/categories');
url.searchParams.set('type', 'articles');

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/categories?' . http_build_query(['type' => 'articles']));
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The list carries NO PARENT: building the tree means reading each record on its own.
$cats = Api::Website()->GetCategories([], ['type' => 'articles'])['data'];

Reading One Category

get/api/v1/admin/website/categories/{id}
Website/GetCategory admin

Returns one category with all its languages.

Response fields data — 9
idintThe category id.
typestringThe category type: articles, references.
parentintThe parent category. Zero says it sits at the root.
rankintWhere it sits in the listing.
statusstringWhether the category is live.
optionsobjectThe category's options. The search-engine preference lives here.
created_atstring | nullWhen it was created.
languagesobjectTitle, address, description and search-engine fields per language.
imagestring | nullThe header image address.
Errors 2
category_not_found404No such category.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/website/categories/3' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch(`https://panel.example.com/api/v1/admin/website/categories/${id}`, {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/website/categories/' . $id);
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 back ONLY here; the tree is built from this field.
$cat    = Api::Website()->GetCategory(['id' => $id])['data'];
$isRoot = $cat['parent'] === 0;

Creating a Category

post/api/v1/admin/website/categories
Website/CreateCategory admin the type is fixed

Opens a new category and writes its languages.

Body 7
typestringreqThe category type: articles, references. It cannot be changed later.
languagesobjectreqThe content per language: title, address, description and search-engine fields. A title is needed in each language you send.
statusstringWhether the category is live. Live by default.
rankintWhere it sits in the listing.
seo_indexintLets search engines index the category.
parentintThe parent category. Zero puts it at the root.
imagestringThe header image to upload in the same call. Either an address or the data itself.
Response fields 201 — data — 9
dataobjectThe category created. Same shape as the detail endpoint.
Errors 6
invalid_type422The category type is not recognised.
languages_required422No language was sent.
title_required422A title is empty in one language.
route_exists422That address is already in use in that language.
create_failed500The category could not be created.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/website/categories' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"type":"articles","languages":{"tr":{"title":"Duyurular"}}}'
const res = await fetch('https://panel.example.com/api/v1/admin/website/categories', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    type: 'articles',
    languages: { en: { title: 'Announcements' } },
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/website/categories');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'type'      => 'articles',
        'languages' => ['en' => ['title' => 'Announcements']],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// To nest a category give the PARENT id; zero puts it at the root.
Api::Website()->CreateCategory([
    'type'      => 'articles',
    'parent'    => $parentId,
    'languages' => ['en' => ['title' => 'Release notes']],
]);

Updating a Category

patch/api/v1/admin/website/categories/{id}
Website/UpdateCategory admin

Changes the category fields and languages you send.

Body 6
languagesobjectreqThe content per language: title, address, description and search-engine fields. A title is needed in each language you send.
statusstringWhether the category is live. Live by default.
rankintWhere it sits in the listing.
seo_indexintLets search engines index the category.
parentintThe parent category. Zero puts it at the root.
imagestringThe header image to upload in the same call. Either an address or the data itself.
Response fields data — 9
dataobjectThe category as it now stands. Same shape as the detail endpoint.
Errors 4
category_not_found404No such category.
title_required422A title was emptied in one language.
route_exists422That address is already in use in that language.
insufficient_scope403The key lacks the required scope.
Request
curl -X PATCH 'https://panel.example.com/api/v1/admin/website/categories/3' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"status":"inactive"}'
const res = await fetch(`https://panel.example.com/api/v1/admin/website/categories/${id}`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ status: 'inactive' }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/website/categories/' . $id);
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']),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Changing the parent MOVES the branch; whatever sits below comes along.
Api::Website()->UpdateCategory(['id' => $id, 'parent' => $newParent]);

Deleting a Category

delete/api/v1/admin/website/categories/{id}
Website/DeleteCategory admin sub-categories go too

Removes a category and every category beneath it.

Response fields data — 3
deletedboolWhether the delete ran.
idintThe id of the category removed.
removedarrayEvery id removed. The sub-categories appear in this list too.
Errors 2
category_not_found404No such category.
insufficient_scope403The key lacks the required scope.
Request
curl -X DELETE 'https://panel.example.com/api/v1/admin/website/categories/3' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch(`https://panel.example.com/api/v1/admin/website/categories/${id}`, {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const { data } = await res.json();
console.log(data.removed);   // [3, 7, 8]
$ch = curl_init('https://panel.example.com/api/v1/admin/website/categories/' . $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);
// Read the RETURNED list to see how many went; a call you read as one can take a branch.
$gone = Api::Website()->DeleteCategory(['id' => $id])['data']['removed'];

Uploading a Category Image

put/api/v1/admin/website/categories/{id}/image
Website/UploadCategoryImage admin

Uploads the category header image, in place of the one before.

Body 1
imagestringreqThe image to upload. A thumbnail is built as well.
Response fields 201 — data — 2
kindstringThe image kind. A category has one kind.
urlstringThe image's public address.
Errors 2
category_not_found404No such category.
insufficient_scope403The key lacks the required scope.
Request
curl -X PUT 'https://panel.example.com/api/v1/admin/website/categories/3/image' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"image":"https://ornek.com/kapak.jpg"}'
const res = await fetch(`https://panel.example.com/api/v1/admin/website/categories/${id}/image`, {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ image: 'https://example.com/cover.jpg' }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/website/categories/' . $id . '/image');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['image' => 'https://example.com/cover.jpg']),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// A category has ONE image; unlike a page, there is no kind to choose.
Api::Website()->UploadCategoryImage(['id' => $id, 'image' => $data]);

Removing a Category Image

delete/api/v1/admin/website/categories/{id}/image
Website/DeleteCategoryImage admin

Removes the category header image.

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

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/website/categories/' . $id . '/image');
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);
// To REPLACE the image there is no need to delete first; the upload takes its place.
Api::Website()->DeleteCategoryImage(['id' => $id]);

Pitfalls

A delete takes the whole branch

Deleting a category also deletes every category beneath it. What you read as removing one record can take a branch of the tree. The removed list in the response tells you what actually went, so read it before counting the call a success. Reading the detail first to see the parent links is the safest course.

The list does not show the tree

The listing endpoint does not return the parent field and gives records in a flat order. You cannot build a tree from it. When you need the hierarchy, read each record from the detail endpoint; there are rarely many categories, so this seldom hurts.

The type is fixed at creation

The category type is part of what a record is, and the update ignores it. A category opened for blog posts will not become one for references. Fixing a category opened under the wrong type means creating a new one and moving what sits inside.

The address is unique per language

The same address can sit side by side in different languages, yet two categories in one language cannot share it. Categories and pages draw on the same address space, so a category can clash with a page as well. Leave the address out and it is built from the title.

Changing the parent moves the branch

Changing the parent on an update moves everything beneath it along. Putting a category under one of its own descendants closes the tree on itself, and that branch drops out of the listings. Before moving one, make sure the target does not sit inside the branch you are moving.

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.