Getting started
Mojimoto is a headless CMS. You model your content as content types with typed fields, your team edits entries, and your site reads them over a fast JSON delivery API — or through the typed @mojimoto/client SDK. It's the source your content lives in; how it looks is entirely up to your front end.
Model
Define content types and fields — references, validations, locales. Your structure, not ours.
Edit
A calm editor with drafts, publish, and full version history for every entry.
Deliver
Read published content (or drafts, for preview) over REST, resolved and typed.
Automate
Webhooks fire on publish — rebuild your static site the moment content changes.
Quickstart
From zero to fetching content.
Create a project & model
In the dashboard, create a project, then a content type — say blog_post with a title (short text) and a body (rich text).
Add an entry and publish it
Open the content type, create an entry, fill the fields, and hit Publish. Drafts stay private until you do.
Create a read token
In Settings → API tokens, create a token with the read ability. Copy it — it's shown once.
Fetch your content
Call the delivery API with the token in the Authorization header.
# list published blog posts curl https://mojimoto.com/api/v1/blog/documents?type=blog_post \ -H "Authorization: Bearer mjmt_your_read_token"
import { createClient } from '@mojimoto/client' const cms = createClient({ endpoint: 'https://mojimoto.com/api/v1', project: 'blog', token: process.env.MOJIMOTO_TOKEN!, }) const { results } = await cms.query({ type: 'blog_post' })
Authentication
Every API request is authenticated with a project-scoped token, created in Settings → API tokens. Tokens are prefixed mjmt_ and shown only once — store them as environment variables, never in client-side code.
Pass the token as a Bearer token in the Authorization header. For security, tokens are only accepted in the header — never in the query string, where they'd leak into logs and browser history.
Authorization: Bearer mjmt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Token abilities
Each token carries one or more abilities, so you can hand a build server read-only access while Claude Code or an integration gets write access.
| Ability | Grants |
|---|---|
read | Read published content from the delivery API. |
preview | Read draft content with ?ref=preview — for staging. |
write | Create / update / publish content & models via the management API and MCP. |
Content models
A content type is the shape of your content: a set of typed fields, grouped into tabs. Types are either repeatable (many entries, each with a slug — e.g. blog posts) or singletons (exactly one — e.g. site settings or a homepage). Build them in the model builder, or edit them over the API.
Field types
Modelled on Contentful's taxonomy — composition happens through references, not page-builder slices.
| Type | Stores |
|---|---|
symbol | Short, single-line text. |
text | Long, multi-line plain text. |
rich_text | Formatted rich text (stored as HTML, sanitised on save). |
number | Integer or decimal. |
boolean | True / false. |
date | Date & time. |
select | One value from a preset list. |
media | A link to an asset in the media library. |
reference | A link to one other entry. |
references | An ordered list of linked entries. |
slug | The URL slug (one per type). |
location | Latitude / longitude. |
json | An arbitrary JSON object. |
References & composition
Instead of slices, you compose pages by referencing other entries. A page might have a sections field of type references that links to reusable hero and article_grid entries. Reference fields declare which content types they may point at, and the delivery API resolves them inline (one level deep) so your front end gets the linked entries' data, not just ids.
Validation & publishing
Fields can be required, length- or range-bounded, or matched against a regex. Required fields are enforced on publish — drafts can be incomplete, but published content is always valid. Every save is a new version, so you have full history and can publish or roll back per locale.
Delivery API
Read content over REST. The base URL is your CMS host plus /api/v1/{project}. Responses are JSON; reference fields are resolved inline.
GET /api/v1/{project}/documents
List documents. Returns only published content unless you request a preview.
type— filter by content type api iduid— filter by sluglang— locale code (defaults to the project default)page,per_page— pagination (max 100)ref=preview— return drafts (needs apreviewtoken)
curl "https://mojimoto.com/api/v1/blog/documents?type=blog_post&uid=hello" \ -H "Authorization: Bearer mjmt_read_token"
{
"results": [
{
"id": 42,
"uid": "hello",
"type": "blog_post",
"lang": "en-gb",
"status": "published",
"published_at": "2026-06-09T08:00:00+00:00",
"data": {
"title": "Hello, world",
"body": "<p>…</p>",
"sections": [ { "type": "hero", "data": { … } } ]
}
}
],
"total_results": 1,
"ref": "published"
}
GET /api/v1/{project}/documents/{id}
Fetch a single entry by id. Add ?ref=preview to read its draft.
Writing content
A token with the write ability can manage content and models under /api/v1/{project}/manage — and the same operations are exposed as an MCP server, so Claude Code can model and edit content directly. Every write is attributed and audit-logged.
POST /manage/documents
Create an entry.
PUT /manage/documents/{id}
Save a draft.
POST /manage/documents/{id}/publish
Publish (validated).
POST /manage/content-types/{type}/fields
Evolve a model.
Preview & production
Saved drafts are visible on staging; published content is visible on production. Same endpoints — your site points at the right one with environment variables: a read token in production, a preview token plus ?ref=preview on staging. Try the toggle:
Webhooks
Add a webhook in Settings → Webhooks to POST to a URL whenever content is published — perfect for triggering a Nuxt/Next rebuild. Payloads are HMAC-signed with the webhook's secret in the X-Mojimoto-Signature header, so you can verify they really came from Mojimoto.
X-Mojimoto-Event: document.published X-Mojimoto-Signature: sha256=… { "event": "document.published", "project": "blog", "document": { "id": 42, "uid": "hello", "data": { … } } }
@mojimoto/client
A tiny, typed wrapper over the delivery API — drop it into Next, Nuxt, or any TypeScript project.
npm install @mojimoto/client
import { createClient } from '@mojimoto/client' export const cms = createClient({ endpoint: process.env.MOJIMOTO_ENDPOINT!, project: process.env.MOJIMOTO_PROJECT!, token: process.env.MOJIMOTO_TOKEN!, preview: process.env.MOJIMOTO_PREVIEW === 'true', // staging only })
// list, filtered and paginated const { results } = await cms.query({ type: 'blog_post', perPage: 12 }) // one entry by slug const post = await cms.byUid('blog_post', 'hello') // one entry by id const hero = await cms.byId(42)
Generated types
Pull per-project TypeScript types so the whole API is autocompleted and type-checked against your real models. The CLI fetches them from your project with a read token — no repo access needed. Find the exact command in your project's Settings.
# pulls types from your project over the delivery API npx @mojimoto/cli types \ --endpoint https://mojimoto.com/api/v1 \ --project blog \ --token $MOJIMOTO_TOKEN > mojimoto.generated.ts
import type { BlogPostData } from './mojimoto.generated' const post = await cms.byUid<BlogPostData>('blog_post', 'hello') post?.data.title // fully typed ✓