Hubs
Content hub / category — hubs group posts into top-level sections with branding and per-language translations.
Service: blog.hubs
Content hub / category. Hubs group posts into top-level sections (e.g. “Security”, “Privacy”) and carry their own branding (icon, color) plus per-language translations.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
— | Primary key. Read-only, auto-generated |
slug |
string |
✅ | URL-safe identifier, unique (format: slug) |
title |
string |
✅ | Display title |
tagline |
string? |
Short one-liner shown on the hub page header. null if unset |
|
icon |
string |
Bootstrap Icons class for the hub badge / hero. Default "bi-grid" |
|
color |
string? |
Brand color (hex); gradient end derived in CSS. null if unset |
|
hub_translation |
HubTranslation[] | Inline translations |
HubTranslation
Per-language overlay for a hub. Translatable fields are nullable so a partial translation overlays onto the base hub without clobbering untranslated fields (null = inherit base).
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
— | Primary key. Read-only. Include when updating existing rows |
source_id |
string |
— | Hub being translated. Read-only, auto-set from parent |
language_id |
string |
✅ | Language of this translation |
title |
string? |
Translated hub title (null = inherit base) |
|
tagline |
string? |
Translated hub tagline (null = inherit base) |
Unique constraint: { source_id, language_id } — one translation per language per hub.
find
List hubs with filtering, sorting, and pagination.
HTTP
Request
GET /v1/blog/hubs?fields=id,slug,title,tagline,icon,color,hub_translation.id,hub_translation.language_id,hub_translation.title,hub_translation.tagline&limit=100
Authorization: Bearer <token>
Response
{
"items": [
{
"id": "1920438291700",
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1",
"hub_translation": [
{
"id": "1920438291705",
"language_id": "2",
"title": "Sécurité",
"tagline": null
}
]
}
],
"total": 1
}
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "blog.hubs.find",
"params": {
"fields": [
"id", "slug", "title", "tagline", "icon", "color",
"hub_translation.id", "hub_translation.language_id",
"hub_translation.title", "hub_translation.tagline"
],
"limit": 100
},
"id": 1
}
Response
{
"jsonrpc": "2.0",
"result": {
"items": [
{
"id": "1920438291700",
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1",
"hub_translation": [
{
"id": "1920438291705",
"language_id": "2",
"title": "Sécurité",
"tagline": null
}
]
}
],
"total": 1
},
"id": 1
}
findOneById
Fetch a single hub by id with full detail including translations.
HTTP
Request
GET /v1/blog/hubs/1920438291700?fields=id,slug,title,tagline,icon,color,hub_translation.id,hub_translation.language_id,hub_translation.title,hub_translation.tagline
Authorization: Bearer <token>
Response
{
"id": "1920438291700",
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1",
"hub_translation": [
{
"id": "1920438291705",
"language_id": "2",
"title": "Sécurité",
"tagline": null
}
]
}
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "blog.hubs.findOneById",
"params": {
"id": "1920438291700",
"fields": [
"id", "slug", "title", "tagline", "icon", "color",
"hub_translation.id", "hub_translation.language_id",
"hub_translation.title", "hub_translation.tagline"
]
},
"id": 2
}
Response
{
"jsonrpc": "2.0",
"result": {
"id": "1920438291700",
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1",
"hub_translation": [
{
"id": "1920438291705",
"language_id": "2",
"title": "Sécurité",
"tagline": null
}
]
},
"id": 2
}
create
Create a new hub. The server assigns id. Inline hub_translation entries without id are created alongside the hub.
HTTP
Request
POST /v1/blog/hubs
Authorization: Bearer <token>
Content-Type: application/json
{
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1",
"hub_translation": [
{ "language_id": "2", "title": "Sécurité", "tagline": null }
]
}
Response
{
"id": "1920438291700",
"slug": "security",
"title": "Security",
"hub_translation": [
{ "id": "1920438291705", "language_id": "2", "title": "Sécurité" }
]
}
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "blog.hubs.create",
"params": {
"document": {
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1",
"hub_translation": [
{ "language_id": "2", "title": "Sécurité", "tagline": null }
]
}
},
"id": 3
}
Response
{
"jsonrpc": "2.0",
"result": {
"id": "1920438291700",
"slug": "security",
"title": "Security",
"hub_translation": [
{ "id": "1920438291705", "language_id": "2", "title": "Sécurité" }
]
},
"id": 3
}
updateOneById
Partial update — only specified fields change; omitted fields are untouched. Nested collections are updated automatically: items with id are updated, items without id are created, items missing from the array are deleted.
HTTP
Request
PATCH /v1/blog/hubs/1920438291700
Authorization: Bearer <token>
Content-Type: application/json
{
"tagline": "Encryption, privacy, and our threat model",
"color": "#6610f2"
}
Response
{
"id": "1920438291700",
"tagline": "Encryption, privacy, and our threat model",
"color": "#6610f2"
}
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "blog.hubs.updateOneById",
"params": {
"id": "1920438291700",
"document": {
"tagline": "Encryption, privacy, and our threat model",
"color": "#6610f2"
}
},
"id": 4
}
Response
{
"jsonrpc": "2.0",
"result": {
"id": "1920438291700",
"tagline": "Encryption, privacy, and our threat model",
"color": "#6610f2"
},
"id": 4
}
replaceOneById
Full replacement — all writable fields must be provided. Unlike updateOneById, omitted fields are reset to defaults. Inline collections (hub_translation) are not accepted — use updateOneById to modify them.
HTTP
Request
PUT /v1/blog/hubs/1920438291700
Authorization: Bearer <token>
Content-Type: application/json
{
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1"
}
Response
{
"id": "1920438291700",
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1"
}
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "blog.hubs.replaceOneById",
"params": {
"id": "1920438291700",
"document": {
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1"
}
},
"id": 5
}
Response
{
"jsonrpc": "2.0",
"result": {
"id": "1920438291700",
"slug": "security",
"title": "Security",
"tagline": "Encryption, privacy, and the threat model",
"icon": "bi-shield-lock",
"color": "#6f42c1"
},
"id": 5
}
update
Batch update multiple hubs in a single transaction (all-or-nothing). See Batch operations.
HTTP
Request
PATCH /v1/blog/hubs
Authorization: Bearer <token>
Content-Type: application/json
{
"items": [
{ "id": "1920438291700", "document": { "color": "#6f42c1" } },
{ "id": "1920438291701", "document": { "color": "#0d6efd" } }
]
}
Response
{
"affected": 2,
"items": [
{ "id": "1920438291700", "color": "#6f42c1" },
{ "id": "1920438291701", "color": "#0d6efd" }
]
}
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "blog.hubs.update",
"params": {
"items": [
{ "id": "1920438291700", "document": { "color": "#6f42c1" } },
{ "id": "1920438291701", "document": { "color": "#0d6efd" } }
]
},
"id": 6
}
Response
{
"jsonrpc": "2.0",
"result": {
"affected": 2,
"items": [
{ "id": "1920438291700", "color": "#6f42c1" },
{ "id": "1920438291701", "color": "#0d6efd" }
]
},
"id": 6
}
deleteOneById
Delete a hub. Posts that belonged to this hub are kept but become uncategorized (hub_id becomes null).
HTTP
Request
DELETE /v1/blog/hubs/1920438291700
Authorization: Bearer <token>
Response
{ "id": "1920438291700", "title": "Security" }
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "blog.hubs.deleteOneById",
"params": { "id": "1920438291700" },
"id": 7
}
Response
{
"jsonrpc": "2.0",
"result": { "id": "1920438291700", "title": "Security" },
"id": 7
}
delete
Batch delete hubs in a single transaction (all-or-nothing). See Batch operations.
HTTP
Request
DELETE /v1/blog/hubs
Authorization: Bearer <token>
Content-Type: application/json
{
"items": [
{ "id": "1920438291700" },
{ "id": "1920438291701" }
]
}
Response
{
"affected": 2,
"items": [
{ "id": "1920438291700" },
{ "id": "1920438291701" }
]
}
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "blog.hubs.delete",
"params": {
"items": [
{ "id": "1920438291700" },
{ "id": "1920438291701" }
]
},
"id": 8
}
Response
{
"jsonrpc": "2.0",
"result": {
"affected": 2,
"items": [
{ "id": "1920438291700" },
{ "id": "1920438291701" }
]
},
"id": 8
}