{
  "slug": "schema-export",
  "title": "Schema Export — webhouse-schema.json",
  "description": "Generate a JSON Schema document from cms.config.ts so PHP, Python, Ruby, Go, Java, and .NET reader libraries can introspect your content model.",
  "category": "consumers",
  "order": 8,
  "locale": "en",
  "translationGroup": "79d92164-3d1d-4fd3-9b4a-9c08ded76384",
  "helpCardId": null,
  "content": "## What is schema export?\n\n@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.\n\nBut 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](https://json-schema.org/draft/2020-12/schema) document with `x-webhouse-*` extension keywords for type hints (richtext, tags, blocks, relations) that JSON Schema doesn't natively support.\n\nThink of it as a **generated lockfile** for your content model — derived from `cms.config.ts`, always committed to git, always kept in sync.\n\n## Why it exists\n\nWithout `webhouse-schema.json`, a Java or PHP developer reading @webhouse/cms content has no way to know:\n\n- What collections exist on this site\n- What fields each collection has\n- Which fields are required vs optional\n- Which fields are richtext (markdown) vs plain text\n- Which collections are translatable, which are pages vs data records\n- What blocks are defined and what their fields look like\n\nWith the schema file, every reader library can introspect the content model, generate types, validate documents, and produce IDE autocomplete — all without parsing TypeScript.\n\nIt's also the foundation for future tooling: type generators, schema diff tools, content validators, and automated migration helpers.\n\n## Three ways to export\n\nThere are three equivalent ways to generate `webhouse-schema.json`. Pick the one that fits your workflow.\n\n### 1. CMS admin UI\n\nThe friendliest option for human editors.\n\n1. Open CMS admin → **Site Settings**\n2. Find the **Schema export** section, just below \"Validate site\"\n3. You have two buttons:\n   - **⬇ Download schema** — saves `webhouse-schema.json` to your browser's Downloads folder. Use this for inspection, sharing, or manual handling.\n   - **💾 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.\n\nAfter clicking Save, you'll see a green confirmation panel showing:\n\n- File size (e.g. \"4.5 KB\")\n- Absolute path where it was written\n- Number of collections and blocks included\n\n### 2. CLI command\n\nThe canonical option for CI/CD pipelines, AI agents (Claude Code, Cursor), and scriptable workflows.\n\n```bash\ncd /path/to/your-project\nnpx cms export-schema --out webhouse-schema.json\n```\n\nFlags:\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--out <path>` | stdout | Write to file instead of stdout |\n| `--baseUrl <url>` | (none) | Sets the `$id` field on the schema |\n| `--pretty` | true | Pretty-print JSON output |\n| `--include-blocks` | true | Include block definitions |\n| `--title <text>` | \"Webhouse Content Schema\" | Custom title |\n\nExample with all options:\n\n```bash\nnpx cms export-schema \\\n  --out webhouse-schema.json \\\n  --baseUrl https://my-site.example.com \\\n  --title \"My Site Content Schema\"\n```\n\n### 3. HTTP API\n\nFor tooling integration. Same backend as the UI buttons.\n\n```bash\n# GET — returns the schema as JSON (browser session required)\ncurl -H \"Cookie: cms-session=...\" \\\n  \"https://localhost:3010/api/cms/registry/export-schema?configPath=/abs/path/to/cms.config.ts\"\n\n# GET with download flag — adds Content-Disposition header for browser download\ncurl -H \"Cookie: cms-session=...\" \\\n  \"https://localhost:3010/api/cms/registry/export-schema?configPath=/abs/path/to/cms.config.ts&download=1\" \\\n  -o webhouse-schema.json\n\n# POST — writes the file to the project root, returns metadata\ncurl -X POST -H \"Cookie: cms-session=...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"configPath\":\"/abs/path/to/cms.config.ts\"}' \\\n  https://localhost:3010/api/cms/registry/export-schema\n```\n\nResponse from the POST endpoint:\n\n```json\n{\n  \"ok\": true,\n  \"path\": \"/Users/cb/projects/my-site/webhouse-schema.json\",\n  \"bytes\": 4567,\n  \"collections\": 2,\n  \"blocks\": 0,\n  \"generatedAt\": \"2026-04-09T12:34:56.789Z\"\n}\n```\n\n## What the schema looks like\n\nA minimal example for a blog with a `posts` collection:\n\n```json\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://my-blog.example.com/webhouse-schema.json\",\n  \"title\": \"Webhouse Content Schema\",\n  \"x-webhouse-version\": \"0.3.0\",\n  \"x-generated-at\": \"2026-04-09T00:00:00.000Z\",\n  \"$defs\": {\n    \"Document\": {\n      \"type\": \"object\",\n      \"required\": [\"slug\", \"status\", \"data\"],\n      \"properties\": {\n        \"slug\": { \"type\": \"string\", \"pattern\": \"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$\" },\n        \"status\": { \"enum\": [\"draft\", \"published\", \"archived\", \"expired\", \"trashed\"] },\n        \"locale\": { \"type\": \"string\" },\n        \"translationGroup\": { \"type\": \"string\", \"format\": \"uuid\" }\n      }\n    }\n  },\n  \"collections\": {\n    \"posts\": {\n      \"allOf\": [{ \"$ref\": \"#/$defs/Document\" }],\n      \"x-webhouse-collection\": {\n        \"name\": \"posts\",\n        \"label\": \"Blog Posts\",\n        \"kind\": \"page\",\n        \"urlPrefix\": \"/blog\",\n        \"translatable\": true\n      },\n      \"properties\": {\n        \"data\": {\n          \"type\": \"object\",\n          \"required\": [\"title\"],\n          \"properties\": {\n            \"title\":   { \"type\": \"string\", \"x-webhouse-field-type\": \"text\" },\n            \"content\": { \"type\": \"string\", \"contentMediaType\": \"text/markdown\", \"x-webhouse-field-type\": \"richtext\" },\n            \"date\":    { \"type\": \"string\", \"format\": \"date\", \"x-webhouse-field-type\": \"date\" },\n            \"tags\":    { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"x-webhouse-field-type\": \"tags\" }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\nThe `x-webhouse-*` extension keywords carry semantic information that JSON Schema lacks:\n\n- `x-webhouse-field-type` — the original field type from `cms.config.ts` (text, richtext, tags, image, blocks, etc.)\n- `x-webhouse-collection` — collection-level metadata (label, kind, urlPrefix, translatable)\n- `x-webhouse-relation` — relation field target collection\n- `x-webhouse-richtext-features` — toolbar whitelist for richtext fields\n- `x-webhouse-allowed-blocks` — which blocks can appear in a `blocks` field\n\nReader 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.\n\n## When to re-export\n\n**Whenever you modify `cms.config.ts`, you must regenerate `webhouse-schema.json`.** Otherwise reader libraries will be out of sync.\n\n| Change | Re-export required? |\n|---|---|\n| Add a new collection | ✅ YES |\n| Remove a collection | ✅ YES |\n| Add a field to an existing collection | ✅ YES |\n| Remove a field | ✅ YES |\n| Rename a field | ✅ YES |\n| Change a field's type (text → richtext) | ✅ YES |\n| Change `urlPrefix`, `kind`, `description` on a collection | ✅ YES |\n| Add a block definition | ✅ YES |\n| Edit a content JSON file (`content/posts/hello.json`) | ❌ No — schema describes shape, not data |\n| Update locale settings | Recommended |\n\n## What to commit to git\n\n```\nproject-root/\n  cms.config.ts          ← always commit\n  webhouse-schema.json   ← always commit (generated, but tracked like a lockfile)\n  content/               ← always commit\n  public/uploads/        ← gitignored (per-deployment media)\n```\n\nThe 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`.\n\n## Security\n\n- **Filename validation:** the API only accepts filenames matching `^[a-zA-Z0-9._-]+\\.(json|yaml|yml)$` to prevent injection.\n- **Path traversal protection:** the output path is validated as a subpath of the project directory.\n- **GitHub-backed configs:** schema export from `github://` configs is not supported yet (Phase 2 of F125).\n- **Auth:** the HTTP API is behind the same session-based auth as the rest of `/api/cms/*`.\n\n## See also\n\n- [Framework-Agnostic Architecture](/docs/framework-agnostic) — the bigger picture\n- [Consume from Java (Spring Boot)](/docs/consume-java)\n- [Consume from C# / .NET](/docs/consume-dotnet)\n- [Consume from Laravel (PHP)](/docs/consume-laravel)\n- [Consume from Django (Python)](/docs/consume-django)\n- [Consume from Rails (Ruby)](/docs/consume-rails)\n- [Consume from Go](/docs/consume-go)\n- [Testing the Consumer Examples](/docs/testing-consumer-examples)\n- **Feature plan:** [F125 — Framework-Agnostic Content Platform](https://github.com/webhousecode/cms/blob/main/docs/features/F125-framework-agnostic-consumers.md)",
  "excerpt": "What is schema export?\n\n@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.\n\nBut the",
  "seo": {
    "metaTitle": "Schema Export — webhouse.app Docs",
    "metaDescription": "Generate webhouse-schema.json from cms.config.ts so PHP, Python, Ruby, Go, Java, and .NET reader libraries can introspect your content model.",
    "keywords": [
      "webhouse",
      "cms",
      "schema",
      "json-schema",
      "framework-agnostic",
      "export"
    ]
  },
  "createdAt": "2026-04-09T00:00:00.000Z",
  "updatedAt": "2026-04-09T00:00:00.000Z"
}