Skip to main content

Content API

Manage rich content (announcements, legal documents, help articles) under a multi-tenant, multi-lingual, category-based model.

Public-facing endpoints

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 gofa may be private (GOFA-only) or shared (visible to other clients). Items owned by another client are always private to 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, body are 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

NameDescription
langPreferred locale: en, zh-Hant, or zh-Hans. Falls back to Accept-Language, then en.

Sample response

GET /api/client/acme/content/public/app-announcement?lang=zh-Hant
{
"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

NameDescription
langPreferred 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/body fields are converted to the multilingual map under the en key by the 20260529-content-i18n-categories-migration script.