webhouse.appwebhouse.appdocs

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.

  1. Open CMS admin → Site Settings
  2. Find the Schema export section, just below "Validate site"
  3. You have two buttons:
- **⬇ Download schema** — saves `webhouse-schema.json` to your browser's Downloads folder. Use this for inspection, sharing, or manual handling.

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

bash
cd /path/to/your-project
npx cms export-schema --out webhouse-schema.json

Flags:

FlagDefaultDescription
--out <path>stdoutWrite to file instead of stdout
--baseUrl <url>(none)Sets the $id field on the schema
--prettytruePretty-print JSON output
--include-blockstrueInclude block definitions
--title <text>"Webhouse Content Schema"Custom title

Example with all options:

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

bash
# 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-schema

Response from the POST endpoint:

json
{
  "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:

json
{
  "$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 from cms.config.ts (text, richtext, tags, image, blocks, etc.)
  • x-webhouse-collection — collection-level metadata (label, kind, urlPrefix, translatable)
  • x-webhouse-relation — relation field target collection
  • x-webhouse-richtext-features — toolbar whitelist for richtext fields
  • x-webhouse-allowed-blocks — which blocks can appear in a blocks field

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.

ChangeRe-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 settingsRecommended

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/*.

See also

Tags:SchemaFrameworksArchitecture
Previous
Framework-Agnostic Architecture
Next
Consume from Java (Spring Boot)
JSON API →Edit on GitHub →