Generate a JSON Schema document from cms.config.ts so PHP, Python, Ruby, Go, Java, and .NET reader libraries can introspect your content model.
What is schema export?
@webhouse/cms is a framework-agnostic content platform. The admin UI is TypeScript, but the content layer is universal: flat JSON files in content/ that can be read by any language. Reader libraries in PHP, Python, Ruby, Go, Java, and .NET all consume the same files.
But these reader libraries can't execute your cms.config.ts (it's TypeScript). They need a language-agnostic description of the content model — what collections exist, what fields they have, what types those fields are. That description is webhouse-schema.json: a JSON Schema draft 2020-12 document with x-webhouse-* extension keywords for type hints (richtext, tags, blocks, relations) that JSON Schema doesn't natively support.
Think of it as a generated lockfile for your content model — derived from cms.config.ts, always committed to git, always kept in sync.
Why it exists
Without webhouse-schema.json, a Java or PHP developer reading @webhouse/cms content has no way to know:
- What collections exist on this site
- What fields each collection has
- Which fields are required vs optional
- Which fields are richtext (markdown) vs plain text
- Which collections are translatable, which are pages vs data records
- What blocks are defined and what their fields look like
With the schema file, every reader library can introspect the content model, generate types, validate documents, and produce IDE autocomplete — all without parsing TypeScript.
It's also the foundation for future tooling: type generators, schema diff tools, content validators, and automated migration helpers.
Three ways to export
There are three equivalent ways to generate webhouse-schema.json. Pick the one that fits your workflow.
1. CMS admin UI
The friendliest option for human editors.
- Open CMS admin → Site Settings
- Find the Schema export section, just below "Validate site"
- You have two buttons:
- 💾 Save to project root — writes webhouse-schema.json directly next to your cms.config.ts. Use this when you want to commit it to git.
After clicking Save, you'll see a green confirmation panel showing:
- File size (e.g. "4.5 KB")
- Absolute path where it was written
- Number of collections and blocks included
2. CLI command
The canonical option for CI/CD pipelines, AI agents (Claude Code, Cursor), and scriptable workflows.
cd /path/to/your-project
npx cms export-schema --out webhouse-schema.jsonFlags:
| Flag | Default | Description |
|---|---|---|
--out <path> | stdout | Write to file instead of stdout |
--baseUrl <url> | (none) | Sets the $id field on the schema |
--pretty | true | Pretty-print JSON output |
--include-blocks | true | Include block definitions |
--title <text> | "Webhouse Content Schema" | Custom title |
Example with all options:
npx cms export-schema \
--out webhouse-schema.json \
--baseUrl https://my-site.example.com \
--title "My Site Content Schema"3. HTTP API
For tooling integration. Same backend as the UI buttons.
# GET — returns the schema as JSON (browser session required)
curl -H "Cookie: cms-session=..." \
"https://localhost:3010/api/cms/registry/export-schema?configPath=/abs/path/to/cms.config.ts"
# GET with download flag — adds Content-Disposition header for browser download
curl -H "Cookie: cms-session=..." \
"https://localhost:3010/api/cms/registry/export-schema?configPath=/abs/path/to/cms.config.ts&download=1" \
-o webhouse-schema.json
# POST — writes the file to the project root, returns metadata
curl -X POST -H "Cookie: cms-session=..." \
-H "Content-Type: application/json" \
-d '{"configPath":"/abs/path/to/cms.config.ts"}' \
https://localhost:3010/api/cms/registry/export-schemaResponse from the POST endpoint:
{
"ok": true,
"path": "/Users/cb/projects/my-site/webhouse-schema.json",
"bytes": 4567,
"collections": 2,
"blocks": 0,
"generatedAt": "2026-04-09T12:34:56.789Z"
}What the schema looks like
A minimal example for a blog with a posts collection:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://my-blog.example.com/webhouse-schema.json",
"title": "Webhouse Content Schema",
"x-webhouse-version": "0.3.0",
"x-generated-at": "2026-04-09T00:00:00.000Z",
"$defs": {
"Document": {
"type": "object",
"required": ["slug", "status", "data"],
"properties": {
"slug": { "type": "string", "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$" },
"status": { "enum": ["draft", "published", "archived", "expired", "trashed"] },
"locale": { "type": "string" },
"translationGroup": { "type": "string", "format": "uuid" }
}
}
},
"collections": {
"posts": {
"allOf": [{ "$ref": "#/$defs/Document" }],
"x-webhouse-collection": {
"name": "posts",
"label": "Blog Posts",
"kind": "page",
"urlPrefix": "/blog",
"translatable": true
},
"properties": {
"data": {
"type": "object",
"required": ["title"],
"properties": {
"title": { "type": "string", "x-webhouse-field-type": "text" },
"content": { "type": "string", "contentMediaType": "text/markdown", "x-webhouse-field-type": "richtext" },
"date": { "type": "string", "format": "date", "x-webhouse-field-type": "date" },
"tags": { "type": "array", "items": { "type": "string" }, "x-webhouse-field-type": "tags" }
}
}
}
}
}
}The x-webhouse-* extension keywords carry semantic information that JSON Schema lacks:
x-webhouse-field-type— the original field type fromcms.config.ts(text, richtext, tags, image, blocks, etc.)x-webhouse-collection— collection-level metadata (label, kind, urlPrefix, translatable)x-webhouse-relation— relation field target collectionx-webhouse-richtext-features— toolbar whitelist for richtext fieldsx-webhouse-allowed-blocks— which blocks can appear in ablocksfield
Reader libraries use these hints to render appropriately — for example, treating richtext fields as markdown that needs to be converted to HTML, or treating blocks arrays as polymorphic content with a _block discriminator.
When to re-export
Whenever you modify cms.config.ts, you must regenerate webhouse-schema.json. Otherwise reader libraries will be out of sync.
| Change | Re-export required? |
|---|---|
| Add a new collection | ✅ YES |
| Remove a collection | ✅ YES |
| Add a field to an existing collection | ✅ YES |
| Remove a field | ✅ YES |
| Rename a field | ✅ YES |
| Change a field's type (text → richtext) | ✅ YES |
Change urlPrefix, kind, description on a collection | ✅ YES |
| Add a block definition | ✅ YES |
Edit a content JSON file (content/posts/hello.json) | ❌ No — schema describes shape, not data |
| Update locale settings | Recommended |
What to commit to git
project-root/
cms.config.ts ← always commit
webhouse-schema.json ← always commit (generated, but tracked like a lockfile)
content/ ← always commit
public/uploads/ ← gitignored (per-deployment media)The schema file is derived from cms.config.ts but should still be committed. It's the contract between the TypeScript admin and non-TS consumers. Treat it like package-lock.json or composer.lock.
Security
- Filename validation: the API only accepts filenames matching
^[a-zA-Z0-9._-]+\.(json|yaml|yml)$to prevent injection. - Path traversal protection: the output path is validated as a subpath of the project directory.
- GitHub-backed configs: schema export from
github://configs is not supported yet (Phase 2 of F125). - Auth: the HTTP API is behind the same session-based auth as the rest of
/api/cms/*.