Base URL: https://api.cornerspot.net
Authentication: requests use a bearer token, for example Authorization: Bearer YOUR_API_KEY.
List help-center categories
GET /c/api/v1/help_center/categories
Returns a paginated list of the team's knowledge-base categories. Supports full-text search over title and description, a status filter for draft (no published_at) vs live (has published_at) categories, and a parent_id filter for navigating the one-level category tree (top-level categories vs the children of a given parent). Requires the kb_read scope and the Knowledge Base module.
Query parameters
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
query | string | Full-text search over category title and description. |
q | string | Alias for query. |
status | string one of draft, live | Filter by publication status: draft (no published_at) or live (published_at present). Omit for all. |
parent_id | string | Filter by parent. Pass a category id to return that category's children, or none (also null/top_level) to return only top-level categories. Omit to return categories at every level. |
page | integer | 1-based page number. |
per_page | integer | Page size (1–100, default 25). |
Code samples
curl --request GET \
--url 'https://api.cornerspot.net/c/api/v1/help_center/categories' \
--header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
console.log(data);
import json
import urllib.request
url = "https://api.cornerspot.net/c/api/v1/help_center/categories"
headers = {
"Authorization": "Bearer YOUR_API_KEY",
}
request = urllib.request.Request(url, headers=headers, method="GET")
with urllib.request.urlopen(request) as response:
data = json.loads(response.read().decode("utf-8"))
print(data)
require "json"
require "net/http"
uri = URI("https://api.cornerspot.net/c/api/v1/help_center/categories")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
puts JSON.parse(response.body)
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/help_center/categories',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
],
]);
$response = curl_exec($curl);
curl_close($curl);
$data = json_decode($response, true);
print_r($data);
Responses
| STATUS | DESCRIPTION |
|---|---|
200 OK | a page of categories |
401 Unauthorized | missing or invalid API key |
402 Payment Required | team's plan does not include the knowledge feature |
403 Forbidden | the key lacks the kb_read scope |
Create a help-center category
POST /c/api/v1/help_center/categories
Creates a new knowledge-base category for the team. title is required. If slug is omitted it is auto-generated from title. icon_kind defaults to lucide. published_at defaults to the current time.
Pass parent_category_id to nest the category under a top-level one. Categories nest at most one level deep: the parent must itself be top-level, otherwise the request returns 422. Requires the kb_write scope and the Knowledge Base module.
Request body
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
category | object | |
category.title | string · required | Required. Category title, max 120 characters. |
category.description | string | Optional description, max 500 characters. |
category.slug | string | URL slug (lowercase alphanumeric + hyphens). Auto-generated from title if omitted. |
category.icon_kind | string one of lucide, media | Icon type. Defaults to lucide. |
category.icon_name | string | Lucide icon name (required when icon_kind is lucide). |
category.icon_asset_id | string | Asset id for the icon image (required when icon_kind is media). |
category.parent_category_id | string | Optional. Id of a top-level category to nest under. The parent must be top-level (one level of nesting max), else 422. |
category.published_at | string | Publication timestamp. Defaults to now. |
Code samples
curl --request POST \
--url 'https://api.cornerspot.net/c/api/v1/help_center/categories' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
}'
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
})
});
const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
})
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
console.log(data);
import json
import urllib.request
url = "https://api.cornerspot.net/c/api/v1/help_center/categories"
payload = json.dumps({
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
}).encode("utf-8")
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
}
request = urllib.request.Request(url, data=payload, headers=headers, method="POST")
with urllib.request.urlopen(request) as response:
data = json.loads(response.read().decode("utf-8"))
print(data)
require "json"
require "net/http"
uri = URI("https://api.cornerspot.net/c/api/v1/help_center/categories")
request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request["Authorization"] = "Bearer YOUR_API_KEY"
request.body = JSON.generate({
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
puts JSON.parse(response.body)
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/help_center/categories',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer YOUR_API_KEY',
],
CURLOPT_POSTFIELDS => json_encode([
'category' => [
'title' => 'string',
'description' => 'string',
'slug' => 'string',
'icon_kind' => 'lucide',
'icon_name' => 'string',
'icon_asset_id' => '1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d',
'parent_category_id' => '1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d',
'published_at' => '2026-01-15T09:30:00Z',
],
]),
]);
$response = curl_exec($curl);
curl_close($curl);
$data = json_decode($response, true);
print_r($data);
Responses
| STATUS | DESCRIPTION |
|---|---|
201 Created | created |
401 Unauthorized | missing or invalid API key |
402 Payment Required | team's plan does not include the knowledge feature |
403 Forbidden | the key lacks kb_write scope |
422 Unprocessable Content | validation errors (e.g. missing title, invalid icon, non-top-level parent) |
Get a help-center category
GET /c/api/v1/help_center/categories/{id}
Returns a single category by id. Requires kb_read and the Knowledge Base module.
Path parameters
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
id | string · required | Category id. |
Code samples
curl --request GET \
--url 'https://api.cornerspot.net/c/api/v1/help_center/categories/abc123' \
--header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories/abc123', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories/abc123', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
console.log(data);
import json
import urllib.request
url = "https://api.cornerspot.net/c/api/v1/help_center/categories/abc123"
headers = {
"Authorization": "Bearer YOUR_API_KEY",
}
request = urllib.request.Request(url, headers=headers, method="GET")
with urllib.request.urlopen(request) as response:
data = json.loads(response.read().decode("utf-8"))
print(data)
require "json"
require "net/http"
uri = URI("https://api.cornerspot.net/c/api/v1/help_center/categories/abc123")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
puts JSON.parse(response.body)
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/help_center/categories/abc123',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
],
]);
$response = curl_exec($curl);
curl_close($curl);
$data = json_decode($response, true);
print_r($data);
Responses
| STATUS | DESCRIPTION |
|---|---|
200 OK | the category |
401 Unauthorized | missing or invalid API key |
404 Not Found | no such category in this team |
Update a help-center category
PATCH /c/api/v1/help_center/categories/{id}
Updates a help-center category. Note: position cannot be changed here — use the reorder endpoint to resequence categories. Requires kb_write and the Knowledge Base module.
Path parameters
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
id | string · required | Category id. |
Request body
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
category | object | |
category.title | string | |
category.description | string | |
category.slug | string | |
category.icon_kind | string one of lucide, media | |
category.icon_name | string | |
category.icon_asset_id | string | |
category.parent_category_id | string | Nest under a top-level category, or null to promote back to top-level. Must reference a top-level category; a category that already has children can't be nested. |
category.published_at | string |
Code samples
curl --request PATCH \
--url 'https://api.cornerspot.net/c/api/v1/help_center/categories/abc123' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
}'
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories/abc123', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
})
});
const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories/abc123', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
})
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
console.log(data);
import json
import urllib.request
url = "https://api.cornerspot.net/c/api/v1/help_center/categories/abc123"
payload = json.dumps({
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
}).encode("utf-8")
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
}
request = urllib.request.Request(url, data=payload, headers=headers, method="PATCH")
with urllib.request.urlopen(request) as response:
data = json.loads(response.read().decode("utf-8"))
print(data)
require "json"
require "net/http"
uri = URI("https://api.cornerspot.net/c/api/v1/help_center/categories/abc123")
request = Net::HTTP::Patch.new(uri)
request["Content-Type"] = "application/json"
request["Authorization"] = "Bearer YOUR_API_KEY"
request.body = JSON.generate({
"category": {
"title": "string",
"description": "string",
"slug": "string",
"icon_kind": "lucide",
"icon_name": "string",
"icon_asset_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"parent_category_id": "1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
"published_at": "2026-01-15T09:30:00Z"
}
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
puts JSON.parse(response.body)
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/help_center/categories/abc123',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer YOUR_API_KEY',
],
CURLOPT_POSTFIELDS => json_encode([
'category' => [
'title' => 'string',
'description' => 'string',
'slug' => 'string',
'icon_kind' => 'lucide',
'icon_name' => 'string',
'icon_asset_id' => '1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d',
'parent_category_id' => '1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d',
'published_at' => '2026-01-15T09:30:00Z',
],
]),
]);
$response = curl_exec($curl);
curl_close($curl);
$data = json_decode($response, true);
print_r($data);
Responses
| STATUS | DESCRIPTION |
|---|---|
200 OK | updated — title changed and nested under a top-level parent |
401 Unauthorized | missing or invalid API key |
403 Forbidden | the key lacks kb_write scope |
404 Not Found | no such category in this team |
422 Unprocessable Content | validation errors (e.g. blank title, invalid slug, non-top-level parent) |
Delete a help-center category
DELETE /c/api/v1/help_center/categories/{id}
Soft-deletes a help-center category. Returns 422 category_has_articles if the category still contains non-deleted articles — move or delete those articles first. Requires kb_write and the Knowledge Base module.
Path parameters
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
id | string · required | Category id. |
Code samples
curl --request DELETE \
--url 'https://api.cornerspot.net/c/api/v1/help_center/categories/abc123' \
--header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories/abc123', {
method: 'DELETE',
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories/abc123', {
method: 'DELETE',
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
console.log(data);
import json
import urllib.request
url = "https://api.cornerspot.net/c/api/v1/help_center/categories/abc123"
headers = {
"Authorization": "Bearer YOUR_API_KEY",
}
request = urllib.request.Request(url, headers=headers, method="DELETE")
with urllib.request.urlopen(request) as response:
data = json.loads(response.read().decode("utf-8"))
print(data)
require "json"
require "net/http"
uri = URI("https://api.cornerspot.net/c/api/v1/help_center/categories/abc123")
request = Net::HTTP::Delete.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
puts JSON.parse(response.body)
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/help_center/categories/abc123',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
],
]);
$response = curl_exec($curl);
curl_close($curl);
$data = json_decode($response, true);
print_r($data);
Responses
| STATUS | DESCRIPTION |
|---|---|
204 No Content | deleted |
401 Unauthorized | missing or invalid API key |
403 Forbidden | the key lacks kb_write scope |
404 Not Found | no such category in this team |
422 Unprocessable Content | category still contains articles |
Reorder help-center categories
POST /c/api/v1/help_center/categories/reorder
Sets the position of every help-center category in one atomic operation. The ids array must contain exactly the ids of every category in the team — a partial or mismatched list returns 422 incomplete_reorder. Requires kb_write and the Knowledge Base module.
Request body
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
ids[] | array of string · required | Complete ordered list of every category id in the team. |
Code samples
curl --request POST \
--url 'https://api.cornerspot.net/c/api/v1/help_center/categories/reorder' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"ids": [
"1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d"
]
}'
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories/reorder', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"ids": [
"1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d"
]
})
});
const data = await response.json();
console.log(data);
// Node.js 18+ (native fetch)
const response = await fetch('https://api.cornerspot.net/c/api/v1/help_center/categories/reorder', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"ids": [
"1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d"
]
})
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
console.log(data);
import json
import urllib.request
url = "https://api.cornerspot.net/c/api/v1/help_center/categories/reorder"
payload = json.dumps({
"ids": [
"1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d"
]
}).encode("utf-8")
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
}
request = urllib.request.Request(url, data=payload, headers=headers, method="POST")
with urllib.request.urlopen(request) as response:
data = json.loads(response.read().decode("utf-8"))
print(data)
require "json"
require "net/http"
uri = URI("https://api.cornerspot.net/c/api/v1/help_center/categories/reorder")
request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request["Authorization"] = "Bearer YOUR_API_KEY"
request.body = JSON.generate({
"ids": [
"1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d"
]
})
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
puts JSON.parse(response.body)
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.cornerspot.net/c/api/v1/help_center/categories/reorder',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer YOUR_API_KEY',
],
CURLOPT_POSTFIELDS => json_encode([
'ids' => [
'1f2e3d4c-5b6a-4789-9c0d-1e2f3a4b5c6d',
],
]),
]);
$response = curl_exec($curl);
curl_close($curl);
$data = json_decode($response, true);
print_r($data);
Responses
| STATUS | DESCRIPTION |
|---|---|
200 OK | reordered — returns the full list in new order |
401 Unauthorized | missing or invalid API key |
402 Payment Required | team's plan does not include the knowledge feature |
403 Forbidden | the key lacks kb_write scope |
422 Unprocessable Content | incomplete reorder — ids do not match all team categories |
