Public beta · free while it lasts · MIT
Your content model is a file. Everything else is generated.
One TypeScript declaration becomes Postgres tables, an editor, a REST and GraphQL API, and a permission set the AI assistant has to obey. It runs on your own Postgres, under MIT, with nothing locked away.
npx create-apograph-app my-cms collections/article.ts
export const article = collection('article', {
publishable: true,
fields: {
title: field.text({ required: true }),
body: field.richtext(),
author: field.relation({ to: () => author })
}
}); -
Postgres
- content_article
- title text
- body jsonb
- author_id uuid
- status draft | published
-
Editor
- Title
- Body
- Author
-
API
- GET /api/v1/content/article REST
- { articles { items { title } } } GraphQL
One permission check
Everyone knocks on the same door
An editor in the admin, the AI assistant, an agent over MCP and a script with an API token all reach content through one guard, in the API, on every route. None of them holds a permission the person behind them does not.
-
The assistant cannot do more than you can
Every run holds exactly the permissions of the person using it. Give it to someone with read-only access and they get a read-only assistant — a property a unit test asserts, not a promise.
-
It asks before it changes anything
A write it has not been allowed stops the run and shows you the call. Allow once, allow for this chat, or refuse. Publishing is never something it can do at all.
-
Outside agents get the same door
Apograph is an MCP server. An agent connects with an API token and works through the same permission-checked tools, so there is one policy to review, not two.
-
You decide which model it uses
Claude, any OpenAI-compatible endpoint, or a model inside your own network so nothing leaves it. Switching is a setting, and the whole feature is off until you switch it on.
Translate the autumn campaign page into German
- Read “Autumn campaign”
- Checked existing translations — none for German
Create translation
“Autumn campaign” · German
Allow once Allow for this chat Don’t allow
Nothing is written until you pick. Permission granted here lasts for this conversation only, and every attempt is recorded — including the ones that were refused.
What you get
Everything included, from day one
No modules to buy and no add-ons to wait for. Four groups, and every feature in the MIT core.
Create
Model it, write it, translate it, and find it again.
- Writing A full-width editor with tables, callouts and media, over a document model that can check its own accessibility.
- Content model Content types are declared in your repository, generate real Postgres tables, and ship with your code.
- Content library Compose filters with and, or and nested groups, reach through relations, and share the exact view you were looking at.
- Media library Folders, assets and alt text, with the bytes on your disk or in your bucket rather than in the database.
- Languages One row per language, with each field marked shared or per-language, and a fallback chain when one is missing.
Ship
Get it live, keep every version, serve it anywhere.
- Publishing A readiness check that runs the server’s own validation, and a status that admits the state everyone else hides.
- Publication protection Branch protection for content: a content type can require N approvals before anything of that kind goes live.
- Version history Every save writes an immutable snapshot of the whole entry, in the same transaction as the change.
- Delivery API REST and GraphQL over one implementation, with the same token, the same permissions and the same visibility rules.
- Segments Decide who may read a published entry. One predicate, enforced on REST, GraphQL and MCP alike — relation hops included.
- Webhooks A signed POST to your endpoint when content changes, sent from a queue that never holds a transaction open for somebody else’s server.
- Export and import Content out in four formats and back in through the same writer every editor uses — one hop deep, matched on natural keys, never past your own permissions.
Run
Who may do what, where, and what happened.
- Roles and permissions Three roles, 34 permission keys, checked in the API rather than in the interface.
- Sign-in and SSO Invite-only accounts, sessions you revoke with a delete, and three identity-provider adapters that replace the credential check without replacing the way in.
- Workspaces Separate brands, clients or markets in one installation, with membership deciding where each person may act.
- Activity and insights A searchable record of who changed what, and a dashboard of how the content itself is doing.
- Content alarms Write down what your team counts as wrong, and see it flagged wherever that content is touched — without blocking anybody.
- Yours to run Your servers, your Postgres database, your files. Nothing calls home, and there is no hosted edition.
Build
AI in the product, agents outside it, and your own code in both.
- Apograph AI An assistant inside the editor that holds exactly your permissions — never more, and never its own.
- Agents and MCP Apograph is an MCP server. An outside agent connects with an API token and works through the same permission-checked tools.
- Plugins Every capability in the product is a plugin on the same public contract your own code uses.
Plugins
An Apograph deployment is its plugin list
There is barely a core. Everything the product does — content, media, translation, the assistant, webhooks — is a plugin on the same public contract your own code uses, registered in the same two arrays.
- Create
- Ship
- Run
- Build
- Every deployment
Two arrays in your app decide what the API serves and what the admin renders. Remove a cell and that capability is gone, with no dead code behind it; add your own on the same contract and it sits in the same list. Each cell links to that plugin's page.
Webhooks
Your stack hears about it
A storefront rebuilds, a search index re-crawls, a cache purges. Register a URL, say which workspaces, events and content types it cares about, and every matching change arrives as a signed POST — from a queue, never from inside the transaction that made it.
- A write commits the event lands in the same transaction
- The queue takes it one row per endpoint that matched
- A worker delivers HMAC-signed, with a stable event id
- Your endpoint answers 2xx and does the work
-
Signed and deduplicated
Every delivery carries an HMAC signature and an event id. Verify one, dedupe on the other, and a retry can never apply twice.
-
Retried on a ladder
Ten seconds, a minute, five, thirty, two hours, six. A receiver that is down for the afternoon still gets its afternoon.
-
Filtered, not firehosed
Workspaces × event kinds × content types. An empty set means everything — including the workspace you create next month.
Who it is for
Three teams that outgrew a hosted CMS
-
An agency with eleven clients
One installation, eleven workspaces, each with its own content, members and dashboard. A client’s editor never sees another client’s work, and the eleventh workspace costs what the first did.
-
A brand in six markets
One row per language, shared fields written once, a fallback chain when a translation is missing, and an assistant that drafts the German while the editor reviews the French.
-
An organisation with a security review
Content in its own Postgres, files on its own disks, single sign-on it can enforce, an activity log of who changed what, four eyes on anything that goes live, and an AI that provably holds no permission of its own.
Straight answers
The questions teams ask first
Answered plainly, in a sentence or two.
Is it really free?
Yes, and free means production — no time limit, no watermark, no licence key, and no cap on people, workspaces, records, API calls or bandwidth. Every feature on this site is in the MIT core and none of them is paid-only. A plan buys support with a response time and a licence agreement, never a capability. The pricing page says exactly that.
How many people can we add?
As many as you like, on every plan — there is no seat count and nothing to buy per user, so invite everyone who touches content. A paid plan covers one organisation, however many people and installations it has.
Where does our content live?
In your own PostgreSQL database, on your own infrastructure, with your files on your own disks. You can query it, back it up and take it with you at any time.
Can non-technical people use it?
Yes. Writers, editors and translators do everything in the admin: creating, editing, translating, uploading and publishing. No one needs to touch code to do their job.
Can we work in more than one language?
Yes. Translation is built in, not an add-on. Each language is a full version of the story, and fields that should read the same everywhere only need writing once.
Can we require somebody else to approve before publishing?
Yes, and per content type rather than for the whole CMS. Switch protection on for a type and entries of that kind need a set number of approvals before they can go live, from somebody other than whoever wrote the version being published. An approval is bound to that version, so editing afterwards asks for it again, and an administrator who overrides the rule leaves a row in the activity log saying so.
What can the AI assistant see?
Only what the person using it can see. It holds exactly that person’s permissions, and it asks for approval in the conversation before it changes anything.
What do we need to run it?
PostgreSQL 16 and Node.js 22. There is nothing else to install and nothing else to keep running — no search cluster, no message broker, no cache. Webhook delivery and the alarm sweep run inside the API process, against your own database.
Ten minutes to a published entry
One command scaffolds the app, the database and the admin. The quick start takes it from there.
npx create-apograph-app my-cms