Mojimoto docs
GitHub ↗ Start free
Developer documentation

Build with Mojimoto.

Model content once, deliver it anywhere through a clean, typed API. Everything you need — auth, models, the delivery API, and the TypeScript SDK.

Overview

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.

5 minutes

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.

GET /api/v1/blog/documents?type=blog_post
# list published blog posts
curl https://mojimoto.com/api/v1/blog/documents?type=blog_post \
  -H "Authorization: Bearer mjmt_your_read_token"
app.ts
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' })
Security

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 header
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.

AbilityGrants
readRead published content from the delivery API.
previewRead draft content with ?ref=preview — for staging.
writeCreate / update / publish content & models via the management API and MCP.
Modelling

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.

TypeStores
symbolShort, single-line text.
textLong, multi-line plain text.
rich_textFormatted rich text (stored as HTML, sanitised on save).
numberInteger or decimal.
booleanTrue / false.
dateDate & time.
selectOne value from a preset list.
mediaA link to an asset in the media library.
referenceA link to one other entry.
referencesAn ordered list of linked entries.
slugThe URL slug (one per type).
locationLatitude / longitude.
jsonAn 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.

API reference

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 id
  • uid — filter by slug
  • lang — locale code (defaults to the project default)
  • page, per_page — pagination (max 100)
  • ref=preview — return drafts (needs a preview token)
GET /api/v1/blog/documents?type=blog_post&uid=hello
curl "https://mojimoto.com/api/v1/blog/documents?type=blog_post&uid=hello" \
  -H "Authorization: Bearer mjmt_read_token"
200 OK
{
  "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.

Workflow

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:

GET /api/v1/blog/documents/42

Automation

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.

POST → your endpoint
X-Mojimoto-Event: document.published
X-Mojimoto-Signature: sha256=…

{
  "event": "document.published",
  "project": "blog",
  "document": { "id": 42, "uid": "hello", "data": { … } }
}
SDK

@mojimoto/client

A tiny, typed wrapper over the delivery API — drop it into Next, Nuxt, or any TypeScript project.

install
npm install @mojimoto/client
cms.ts
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
})
usage.ts
// 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.

terminal
# 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
typed.ts
import type { BlogPostData } from './mojimoto.generated'

const post = await cms.byUid<BlogPostData>('blog_post', 'hello')
post?.data.title // fully typed ✓

Start free → Back to home