Features Apograph CMS on GitHub

HTTP endpoints

Every route, grouped by surface, with what authenticates each one.

Documents 0.5.2 Updated Edit this page Report a problem

On this page

Two surfaces, and the difference matters:

SurfacePrefixAuthenticates with
Public API/api/v1/...An API token, as a bearer
Admin API/api/...A session cookie, plus RBAC

Build integrations against /api/v1. The admin API is the application’s own and is not versioned.

A running server serves the exhaustive generated document at /reference, with raw OpenAPI at /reference/json.

Public API

Content

MethodPath
GET/api/v1/content-types
GET/api/v1/content-types/:name
GET/api/v1/content/:type
GET/api/v1/content/:type/:id
GET/api/v1/content/:type/:id/relations/:field
GET/api/v1/content/:type/:id/media
GET/api/v1/content/:type/:id/translations
GET/api/v1/content/:type/group/:groupId
GET/api/v1/content/:type/group/:groupId/relations/:field
GET/api/v1/content/:type/group/:groupId/media
GET/api/v1/content/:type/group/:groupId/translations
POST/api/v1/content/:type
PATCH/api/v1/content/:type/:id
POST/api/v1/content/:type/:id/publish
POST/api/v1/content/:type/:id/unpublish
DELETE/api/v1/content/:type/:id
PATCH/api/v1/content/:type/group/:groupId
POST/api/v1/content/:type/group/:groupId/publish
POST/api/v1/content/:type/group/:groupId/unpublish
DELETE/api/v1/content/:type/group/:groupId
POST/api/v1/content/:type/bulk
POST/api/v1/content/:type/bulk/publish
POST/api/v1/content/:type/bulk/unpublish
POST/api/v1/content/:type/bulk/delete

Media, GraphQL and MCP

MethodPathNotes
POST/api/v1/media/assetsMultipart upload
GET/api/v1/media/assets/:id/rawStreams bytes; ?variant=
POST/api/v1/graphqlQueries and mutations
GET/api/v1/graphqlThe schema as SDL
GET/api/v1/graphql/playgroundGraphiQL
ALL/api/v1/mcpOff unless MCP_ENABLED=true
GET/api/v1/content/:type/:id/accessAn entry’s audiences. segments:read
PUT/api/v1/content/:type/:id/accessSets them. segments:manage

Every read above is filtered by reader entitlements when the segments plugin holds any audience — including on each relation hop, inside both the page and its total.

Admin API

Authentication and account

MethodPath
POST/api/auth/login, /api/auth/logout
GET/api/auth/me
GET/api/auth/invite/:token
POST/api/auth/invite/accept
GET/api/auth/reset/:token
POST/api/auth/reset
GET, PUT/api/preferences
GET/api/users/:id/sessions
DELETE/api/users/:id/sessions/:sessionId

Single sign-on

Added in 0.4.0. All three of the first routes are public and rate-limited; the callback carries no origin guard, because it is a top-level GET from a third party with no Origin header. See single sign-on.

MethodPathWhat
GET/api/auth/ssoThe registered providers, for the sign-in buttons. [] when none
GET/api/auth/sso/:provider/startOpens an attempt and redirects to the provider
GET/api/auth/sso/:provider/callbackVerifies, opens a session, redirects into the admin
POST/api/auth/sso/:provider/callbackThe same, for SAML’s form post
POST/api/auth/sso/:provider/backchannel-logoutEnds sessions because the provider said so

Content, revisions and schema

MethodPath
GET/api/content-schema, /api/content-schema/:name
GET/api/content-schema/:name/filter-fields
GET/api/content/:type, /api/content/:type/:id
GET/api/content/:type/:id/media, /api/content/:type/:id/relations
POST/api/content/:type
PATCH/api/content/:type/:id
POST/api/content/:type/:id/publish, /unpublish, /restore
DELETE/api/content/:type/:id, /api/content/:type/:id/permanent
POST/api/content/:type/bulk/publish, /publish/preview, /unpublish, /delete, /restore, /purge
GET/api/content/:type/:id/revisions, /revisions/:number
POST/api/content/:type/:id/revisions/:number/restore, /publish

Saved views

Named slices of a content list. The whole surface needs content:read; sharing one with the workspace additionally needs views:share, checked per write. See saved views.

MethodPathWhat
GET/api/viewsEvery view the caller may see, for a scope
POST/api/viewsSaves the current slice under a name
PATCH/api/views/:idRenames, re-slices or changes visibility
DELETE/api/views/:idRemoves it
PUT/api/views/:id/defaultMakes it the caller’s default for that list
DELETE/api/views/:id/defaultClears that default

Export and import

Added in 0.4.0, and gated on content:export / content:import — both of which a contributor holds. See export and import.

MethodPathWhat
POST/api/content/:type/export/previewThe counts, from the same graph walk the export runs
POST/api/content/:type/exportThe document, or an archive when files are included
POST/api/content/:type/import/previewThe verdicts. Writes nothing
POST/api/content/:type/importApplies them
GET/api/content/:type/import/templateAn empty file of the right shape. content:read

Media, users, workspaces, tokens, activity

MethodPath
GET/api/media/assets, /api/media/folders
POST/api/media/assets, /api/media/folders
PATCH/api/media/assets/:id, /api/media/folders/:id
POST/api/media/assets/:id/duplicate
GET/api/media/assets/:id/raw
DELETE/api/media/assets, /api/media/folders/:id
GET/api/users, /api/users/:id
POST/api/users/invites, /api/users/:id/invites/resend
POST/api/users/:id/disable, /enable, /password-reset
PATCH/api/users/:id
DELETE/api/users/:id/invites
GET/api/workspaces, /api/workspaces/slug-available
POST/api/workspaces, /api/workspaces/:id/archive, /unarchive
PATCH/api/workspaces/:id
DELETE/api/workspaces/:id
POST/api/workspaces/:id/members, /api/workspaces/:id/content
DELETE/api/workspaces/:id/members/:userId, /api/workspaces/:id/content/:slug
GET/api/workspaces/:id/entry-count, /api/workspaces/:id/content/:slug/entry-count
GET/api/content-types
GET, POST/api/api-tokens
DELETE/api/api-tokens/:id
GET/api/activity, /api/activity/entries/:entryId
GET/api/activity/dead-letters

/api/activity/dead-letters lists the outbox events that exhausted their attempts — most often an audit row that was never written. Same key as the log, activity:read; see the activity plugin.

Alarms

Content rules and their findings, all workspace-scoped. Reads need alarms:read; every write needs alarms:manage. See alarms.

MethodPathWhat
GET/api/alarms/rulesThe workspace’s rules
POST/api/alarms/rulesCreates one, and scans immediately
POST/api/alarms/rules/previewHow many entries a filter would match
PATCH/api/alarms/rules/:idEdits one; a filter change forces a rescan
DELETE/api/alarms/rules/:idRemoves it and its findings
POST/api/alarms/rules/:id/rescanRe-evaluates the whole collection
GET/api/alarms/findingsThe open findings, paged
GET/api/alarms/findings/by-entryFindings for up to 100 entry ids
GET/api/alarms/findings/summaryThe severity tally for the whole set

Segments

Reader audiences and per-entry access. See segments.

MethodPathWhat
GET/api/segmentsThe audience directory. segments:read
POST/api/segmentsCreates one. segments:manage
GET/api/segments/lookupResolves ids to names. segments:read
GET/api/segments/:idOne audience. segments:read
PATCH/api/segments/:idEdits it. segments:manage
DELETE/api/segments/:idRemoves it. segments:manage
GET/api/segments/entries/:entryIdOne entry’s two lists. segments:read
PUT/api/segments/entries/:entryIdSets them. segments:manage

The admin does not use that PUT. It sends the audiences in the entry save’s extensions bag, so the access row commits on the save’s own transaction and is captured by the revision that save appends.

Publication protection

Rules, reviews and the reviewer’s queue, all workspace-scoped. Reading where an entry stands needs content:read; asking for a review needs content:update; voting needs content:approve; the rules themselves need protection:manage. See publication protection.

MethodPathWhat
GET/api/protection/rulesEvery rule in the workspace. protection:manage
PUT/api/protection/rules/:kind/:slugWrites one. 404 on a type the workspace was not granted
DELETE/api/protection/rules/:kind/:slugRemoves it. Same effect as disabling, with no row left
GET/api/protection/entries/:type/:idThe requirement, the approvals with their staleness, the open request, and the verdict a save by this caller would meet
GET/api/protection/entries/:type/:id/reviewersWho may be asked: the other members holding content:approve
POST/api/protection/entries/:type/:id/requestOpens the request, or replaces who the open one names
DELETE/api/protection/entries/:type/:id/requestWithdraws it. Requester or administrator
POST/api/protection/entries/:type/:id/approveRecords this caller’s approval of the current version
DELETE/api/protection/entries/:type/:id/approveWithdraws their own
GET/api/protection/entries/:type/statusThe review state of many entries at once, for the records column
GET/api/protection/types/:typeWhat publishing a new entry of the type would meet, for the create form
GET/api/protection/queueOpen requests across every type; ?mine=1 for the ones naming the caller

A blocked publish answers 409protection.insufficient_approvals, with the numbers, or protection.token_refused for a bearer token on a type that does not allow one — never 403. The two have to be told apart at the other end: 403 means ask an administrator for a permission, 409 means ask a colleague to read your work. Publishing past a rule is the ordinary publish call plus { bypass: true }, and it is a 403 without admin_bypass or without being an administrator.

The routes do not hang off /api/content/... on purpose: a plugin mounting into another plugin’s path makes the prefix ambiguous to read and to guard.

Webhooks

Outgoing endpoints and their delivery log. Global, not workspace-scoped — there is no workspace guard on any of them — and both keys are admin-only. Every write carries the origin guard. See webhooks.

MethodPathWhat
GET/api/webhook-eventsThe subscribable event catalogue. webhooks:read
GET/api/webhooksEvery endpoint, with its last delivery. webhooks:read
POST/api/webhooksCreates one and returns the secret, once. webhooks:manage
GET/api/webhooks/:idOne endpoint, with secretHint only. webhooks:read
PATCH/api/webhooks/:idReplaces its configuration. webhooks:manage
DELETE/api/webhooks/:idRemoves it and its log. webhooks:manage
POST/api/webhooks/:id/secretRotates the secret and returns the new one, once. webhooks:manage
POST/api/webhooks/:id/testSends a synchronous ping and reports the response. webhooks:manage
GET/api/webhooks/:id/deliveriesOne page of the log; ?status=, ?eventKind=. webhooks:read
GET/api/webhooks/:id/deliveries/:deliveryIdOne delivery, with the frozen body and the response snippet. webhooks:read
POST/api/webhooks/:id/deliveries/:deliveryId/redeliverQueues the same event again, same event id. webhooks:manage

A rejected URL answers 422, not 400: the request was well-formed and the value was refused by policy, and the message is written to be shown in the form that produced it. A delivery id from another endpoint answers 404 — the log is not a way to enumerate deliveries across endpoints.

Localization, insights and copilot

MethodPath
GET/api/i18n/locales
GET/api/i18n/content/:type/:id/locales
POST/api/i18n/content/:type/locale-summary
GET/api/insights/content/{totals,pipeline,velocity,stale,unshipped,punchcard}
GET/api/insights/media/{storage,uploads,alt}
GET/api/insights/i18n/coverage
GET/api/insights/protection/reviews
POST/api/copilot/runs
GET/api/copilot/conversations, /api/copilot/conversations/:id
PATCH/api/copilot/conversations/:id
POST/api/copilot/runs/:runId/permission, /permission/extend
GET/api/copilot/models, /api/copilot/proposals, /api/copilot/proposals/:id
GET, POST/api/copilot/skills
GET/api/copilot/skills/manage, /api/copilot/skills/:id
PATCH, DELETE/api/copilot/skills/:id

Not under a prefix

PathWhat
/referenceThe interactive API reference
/reference/jsonThe raw OpenAPI document

These sit outside the global /api prefix.