Core concepts
Workspaces, memberships, content entities, and the headless delivery model.
Clog is a headless, multi-tenant blog CMS. You author content in a dashboard and deliver it through a JSON API. There is no public-facing blog rendered by Clog — consumers fetch content and render it on their own infrastructure (a marketing site, an app, a docs portal).
This page introduces the concepts you'll meet on every other page: Workspace, Membership, authentication, the content model, and the headless delivery layer.
Workspace = blog
A Workspace is a single blog and the tenant boundary in Clog. Everything else — posts, pages, categories, tags, authors, media, members, API keys — is scoped underneath one workspace. A user can create and belong to many workspaces; there is no level above (no parent "Organization") and no level below (one workspace is one blog).
Workspace slugs are unique platform-wide. Every other slug (post, page, category, tag, author) is unique only within its workspace, so two workspaces can both have a /about page or a news category.
Membership and permissions
You join a workspace through a Membership. Every membership carries:
- A role — either
owner(one per workspace, holds every permission implicitly) ormember. - A list of permissions — explicit grants for members. Ignored for owners.
The permission vocabulary is a fixed, closed set:
| Scope | Read | Write |
|---|---|---|
| Posts | posts:read | posts:write |
| Pages | pages:read | pages:write |
| Categories | categories:read | categories:write |
| Tags | tags:read | tags:write |
| Authors | authors:read | authors:write |
| Media | media:read | media:write |
| Members | members:read | members:write |
| API keys | api-keys:read | api-keys:write |
| Settings | settings:read | settings:write |
| SEO | seo:read | seo:write |
The seo:* scopes are cross-cutting: they let a holder edit the SEO meta group (title, description, canonicalUrl, focus keyword, robots, OG, Twitter) on every entity without needing the entity's own :write scope. Editing the body or other content fields still requires the entity-specific write.
Owner-only actions cannot be granted to a member: deleting the workspace, transferring ownership, and editing the current owner's permission list. To strip owner power, transfer ownership first.
Authentication
The external API at /api/v1/external/* is authenticated with a workspace-bound API key sent in the X-API-Key header.
API keys are bound to one workspace AND one creator user. They carry no permissions of their own — at request time, the key inherits the creator's current Membership permissions on that workspace.
Revoking a member's Membership instantly disables every API key they created on that workspace — without touching the ApiKey rows. For machine integrations, issue keys from a dedicated service user so a teammate leaving doesn't break production.
The full header contract and error semantics live in API → Authenticating requests.
Content entities
Every content entity is workspace-scoped. The shapes you'll fetch from the external API:
| Entity | What it is |
|---|---|
| Post | Block-based article. Status flow draft → published → archived. Carries a bodyJson (array of typed blocks), derived bodyText / tocItems / readingTimeMin, an seo meta group, a schemaType plus structuredData for Schema.org variants, and an embedded author / category / featuredMedia / tags. |
| Page | Simpler than a post — block body, status limited to draft | published, no categorization, no author. Has a pageType (WebPage, AboutPage, ContactPage, LearningResource, MedicalWebPage). |
| BlogCategory | Hierarchical (one optional parent), workspace-scoped, with a materialised path for breadcrumbs. |
| Tag | Flat, workspace-scoped. |
| Author | Persona record for post bylines. Not linked to platform users — just a displayName, bio, avatarMedia, and a person JSON-LD scaffold. |
| Media | A file stored in object storage. Public url, MIME type, optional alt / caption. |
Post and Page bodies are arrays of blocks — typed objects discriminated by a type field. v1 ships 13 block types:
key_takeaways, paragraph, heading, list, quote, divider, image, video_embed, tweet_embed, callout, code, faq, cta.
See API → Rendering content and Reference → Block types for the full per-block schema.
Headless delivery
Clog stores content once and returns it shaped for consumers. A single GET /external/posts/:idOrSlug delivers everything you need to render an article:
- The block array (
bodyJson), with image blocks pre-enriched with a resolved publicurl— no follow-up media lookups. - The embedded
author(and itsavatarMedia),category(with the breadcrumbpathresolved),featuredMedia, andtags. - The
seometa group plus a pre-assembledjsonLdSchema.org graph — drop it into a<script type="application/ld+json">tag. - For posts with structured content (recipes, how-tos, reviews, events, ...): a derived
structuredBlocksarray in the same 13-block vocabulary, so you render the recipe card or how-to list with the same block renderer you already use forbodyJson.
Data is entered once in the dashboard and served three ways from one source — your reader UI (blocks), the crawler view (JSON-LD), and an optional typed payload if you want to build a bespoke widget.
Base URL and routes
The external API lives under /api/v1/external/* at https://api.clog.dev. Clog is a hosted service — there's no self-installed variant, and the base URL is the same for every consumer. Which workspace you read from is decided by the API key in the request, not by the host.
https://api.clog.dev/api/v1/external/posts
https://api.clog.dev/api/v1/external/posts/my-first-post
https://api.clog.dev/api/v1/external/redirectsAll external routes are flat: the workspace is implicit from the bound API key, so paths never carry a :workspaceId segment. Single-resource routes accept either a UUID (resolved by id) or any other string (resolved by slug) — that's the idOrSlug convention you'll see throughout the reference.
Where to next
- Quickstart — issue a key and fetch your first post in five minutes.
- Architecture — the public API surface in more detail.
- API → Authenticating requests — the header contract and error model.