Content API
Manage rich content (announcements, legal documents, help articles) under a multi-tenant, multi-lingual, category-based model.
For SDK integrators the two most useful endpoints are:
GET /api/client/{clientId}/content/public/{key}— fetch active content by well-known key.GET /api/content/{contentId}/public— fetch a single content item by ID (used by the public/ct/{contentId}page).
Both endpoints support a ?lang= query parameter and Accept-Language header for locale resolution.
Data model overview
- Owner: every content item belongs to one owner client (
ownerClientId). - Visibility: items owned by
gofamay beprivate(GOFA-only) orshared(visible to other clients). Items owned by another client are alwaysprivateto that client. - Categories (
categoryKey): each item belongs to exactly one category. Categories also carry default permissions controlling what each non-owner client can do. - Languages:
name,description,title,bodyare stored as{ "en": string; "zh-Hant"?: string; "zh-Hans"?: string }. The public endpoints resolve to a single locale.
Public endpoint: fetch by key
GET /api/client/{clientId}/content/public/{key}
Returns active content for the calling client, including GOFA-shared content visible via category permissions.
Query parameters
| Name | Description |
|---|---|
lang | Preferred locale: en, zh-Hant, or zh-Hans. Falls back to Accept-Language, then en. |
Sample response
{
"success": true,
"locale": "zh-Hant",
"data": [
{
"contentId": "abc123",
"ownerClientId": "gofa",
"clientId": "acme",
"key": "app-announcement",
"categoryKey": "announcements",
"version": 4,
"versionId": "v4",
"locale": "zh-Hant",
"title": "新功能上線",
"body": "我們推出了 ...",
"format": "markdown",
"publishAt": "2026-05-29T00:00:00.000Z",
"expiresAt": null,
"metadata": { "actionUrl": "https://...", "actionLabel": "了解更多" }
}
]
}
Public endpoint: fetch by ID
GET /api/content/{contentId}/public
Unauthenticated endpoint backing the public /ct/{contentId} page (terms, disclaimers, shared documents).
Query parameters
| Name | Description |
|---|---|
lang | Preferred locale. Same resolution as above. |
Sample response
{
"success": true,
"locale": "en",
"data": {
"contentId": "abc123",
"ownerClientId": "gofa",
"categoryKey": "legal",
"key": "terms-and-conditions",
"title": "Terms and Conditions",
"body": "...",
"format": "markdown",
"publishAt": "2026-01-01T00:00:00.000Z",
"expiresAt": null
}
}
Multi-key lookup
GET /api/client/{clientId}/content/public?keys=k1,k2,k3 (max 10)
Returns a map of key → content array, useful for bootstrapping multiple banners/announcements in one round-trip.
Categories
GET /api/client/{clientId}/content-categories — list categories visible to the caller. Always returns the four built-in categories (legal, announcements, help, other) plus any custom categories defined by GOFA.
Category mutations (POST, PATCH, DELETE) require GOFA admin privileges.
Migration notes
- The legacy
/legal/{slug}public URL is removed. Use/ct/{contentId}instead. - Legacy single-string
name/title/bodyfields are converted to the multilingual map under theenkey by the20260529-content-i18n-categories-migrationscript.