GraphQL API
CM Box exposes a GraphQL API for reading content. Unlike a fixed API, the schema is generated per repository from that repository’s content types, so the types and queries you see in introspection match your content model exactly. This page explains where the endpoints live, how the schema is built, how requests are authorized, and how to query the version of content you actually want.
One endpoint per repository
Section titled “One endpoint per repository”Each content repository has its own endpoint:
POST /api/graphql/<repository><repository> is the repository’s name (its namespace). Every repository
runs its own Apollo Server instance with its own schema, so a type that
exists in one repository does not appear in another repository’s schema.
When an administrator changes a repository’s configuration or content types,
that repository’s server is reloaded and its schema regenerated — other
repositories are unaffected.
Because each schema is self-describing, standard GraphQL introspection (and any GraphQL IDE pointed at the endpoint) is the quickest way to see the exact types and fields available in your repository.
How the schema is generated
Section titled “How the schema is generated”For every content type registered on the repository, the generator emits a GraphQL object type containing:
- System fields — present on every type:
id,slug,name,type,status,description,language,createdDate,updatedDate,createdByUser,updatedByUser,taxonomies,renditions,publishedVersion,currentVersion,latestVersion,isLatestVersion,isLatestPublished,isPublished,isArchived,isPreview,autoPublishDate,autoArchiveDate,parent, and more. See the content model for what each one means. - One field per content-type field, with the field’s datatype mapped to
a GraphQL type (for example, text fields become
Stringand number fields becomeInt). Dashes are stripped from field names and spaces become underscores. - Reference fields as nested objects. A reference field that targets a
single content type resolves to that type; a reference field that targets
several types resolves through a generated union named
<Type>_<fieldName>. Reference fields are emitted as lists — even a reference configured to hold a single item returns a list unless your repository is configured to override it to a single object — so you can traverse from an item to its referenced items in one query.
The generator also emits an AllContent union of every type in the
repository (used by the items search query) and a set of standard types:
Taxonomy, Category, GenericContent, and the QuerySettings input.
Query naming conventions
Section titled “Query naming conventions”Generated queries follow consistent names:
| Query | Returns | Purpose |
|---|---|---|
get<Type>(id: ID, slug: String, settings: QuerySettings) |
<Type> |
One item of a specific type, by ID or slug |
items(id: ID, slug: String, settings: QuerySettings) |
{ count, items: [AllContent] } |
Search across the repository’s types |
getItem(id: ID, slug: String, settings: QuerySettings) |
AllContent |
One item when you don’t know its type |
types |
[String] |
The content type names in this repository |
taxonomies(shortName: String) |
[Taxonomy] |
The repository’s taxonomies |
getTaxonomy(shortName: String!) |
Taxonomy |
One taxonomy with its categories |
getCategory(apiName: String!) |
Category |
One category by API name |
tagCloudSearch(field: String!, label: String!, settings: QuerySettings) |
JSON |
Value counts for a field (for example, counts by type or status) over a search |
CM Box’s naming convention reserves create<Type>, update<Type>, and
delete<Type> for mutations. The generated schemas expose read queries
only — create, update, and delete content through the REST API (see the
REST API Reference section in the sidebar).
QuerySettings is the input object shared by all search-style queries:
input QuerySettings { query: String start: Int limit: Int filters: JSON types: [String] status: [String] sort: String advancedQuery: String folder: String version: String showArchive: Boolean showArchiveOnly: Boolean packageName: String packageOnly: Boolean includeFolders: Boolean}Each field’s behavior is described in Query settings.
Authentication
Section titled “Authentication”GraphQL requests are authorized the same way as REST requests: with a JWT sent as a bearer token (see Authentication for how to obtain one):
Authorization: Bearer <token>Browser sessions can also present the JWT in a token cookie. What happens
next depends on the repository:
- Valid token — the request runs as your identity. Search results are filtered by the read permissions of your roles, and you can see draft content.
- No valid token, public repository — repositories marked public accept
anonymous requests. List queries (
items) and reference-field traversal are forced to the latest published, non-archived versions of content. Do not rely on single-item queries to apply the same filtering — always passversion: "latestPublished"explicitly in anonymous integrations. - No valid token, private repository — resolvers return a GraphQL error
with the code
UNAUTHENTICATEDand HTTP status401.
latest vs. latestPublished
Section titled “latest vs. latestPublished”Every save of an item creates a new version, and draft versions coexist with
published ones. QuerySettings.version selects which version a query
returns:
version value |
Meaning |
|---|---|
omitted or "latest" |
The most recent version, including unpublished drafts — what an editor sees |
"latestPublished" |
The most recent published version — what site visitors see |
a version number, e.g. "1.0" |
That specific version |
Choose deliberately: an integration that renders public content should pass
"latestPublished", while an editorial tool usually wants the default
latest. The lifecycle behind these states is explained in
Content model.
Example: fetch one item by ID
Section titled “Example: fetch one item by ID”Assuming a repository with an Article content type, getArticle returns a
single item. This example requests only system fields, which exist on every
generated type:
query ArticleById($id: ID, $settings: QuerySettings) { getArticle(id: $id, settings: $settings) { id name slug status currentVersion publishedVersion isLatestPublished updatedDate }}{ "id": "ITEM_5D41402ABC4B2A76B9719D911017C592", "settings": { "version": "latestPublished" }}Example: search with items
Section titled “Example: search with items”items searches across the repository and returns a count plus a page of
results. Because it returns the AllContent union, select fields with
inline fragments per type:
query PublishedArticles($settings: QuerySettings) { items(settings: $settings) { count items { ... on Article { id name slug type updatedDate } } }}{ "settings": { "query": "annual report", "types": ["Article"], "status": ["published"], "start": 0, "limit": 20, "sort": "updatedDate:DESC" }}If types is omitted, the search spans all of the repository’s content
types (folders are excluded unless you ask for them — see
Folders). When no full-text query is
given and no sort is specified, results are sorted by updatedDate
descending.
Related
Section titled “Related”- Content model — system fields, status
lifecycle, taxonomies, and the full
QuerySettingsbehavior - Authentication — obtaining the bearer token
- Architecture overview — where GraphQL sits in the platform