{"openapi":"3.1.0","info":{"title":"Lucidity Query API","description":"Public content API for Lucidity workspaces.\n\nCreate an API key in **Studio → API keys**. Each key is scoped to one workspace.\n\nAuthenticate with either:\n- `Authorization: Bearer luc_…`\n- `X-Api-Key: luc_…`\n\nOnly **published** documents are returned from `/api/query`.\nCollection fields on pages expand to `{ type, items }` unless `expand=false`.\n\nHTML templates live in Studio → Templates. Render static pages with `/api/render`\nor `npx tsx scripts/render-page.ts` (JSON-LD via `{{{jsonLd}}}`).","version":"1.0.0"},"servers":[{"url":"https://lucidity-lac.vercel.app","description":"Current environment"}],"tags":[{"name":"Health","description":"Service status"},{"name":"Content","description":"Published documents for your workspace"},{"name":"Schemas","description":"Content type definitions for your workspace"},{"name":"Templates","description":"HTML templates and static page rendering"}],"paths":{"/api/health":{"get":{"tags":["Health"],"summary":"Health check","description":"Returns whether the service is up and which env vars are present (no secrets).","operationId":"getHealth","security":[],"responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/api/query":{"get":{"tags":["Content"],"summary":"Query published documents","description":"Returns published documents for the API key’s workspace.\n\n- Without `slug`: array of documents\n- With `slug` and one match: a single document object\n\nCollection fields expand to an array of `{ type, relation, items }` by default.","operationId":"queryDocuments","parameters":[{"name":"type","in":"query","required":false,"description":"Schema type API name (e.g. `post`, `page`, `author`)","schema":{"type":"string","example":"post"}},{"name":"slug","in":"query","required":false,"description":"Document slug. When provided and unique, returns a single object.","schema":{"type":"string","example":"hello-world"}},{"name":"limit","in":"query","required":false,"description":"Max documents to return (1–100). Default 50.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"expand","in":"query","required":false,"description":"Expand collection fields into published items. Default `true`.","schema":{"type":"string","enum":["true","false"],"default":"true"}},{"name":"projectId","in":"query","required":false,"description":"Alias of `workspace`. With an API key, must match the key’s workspace.","schema":{"type":"string"}},{"name":"workspace","in":"query","required":false,"description":"Workspace slug, name, or id (e.g. `web-development-sheffield`). Must match the API key’s workspace.","schema":{"type":"string","example":"web-development-sheffield"}}],"responses":{"200":{"description":"Published document or list of documents","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Document"},{"type":"array","items":{"$ref":"#/components/schemas/Document"}}]},"examples":{"list":{"summary":"List of posts","value":[{"_type":"post","_id":"clx123","_publishedAt":"2026-08-11T12:00:00.000Z","title":"Hello world","slug":"hello-world","excerpt":"First post"}]},"pageWithCollection":{"summary":"Blog page with expanded posts + one author","value":{"_type":"page","_id":"clx456","title":"Blog","slug":"blog","body":"Latest writing","items":[{"type":"post","relation":"all","items":[{"_type":"post","_id":"clx123","_publishedAt":"2026-08-11T12:00:00.000Z","title":"Hello world","slug":"hello-world"}]},{"type":"author","relation":"one","documentId":"clxauthor1","items":[{"_type":"author","_id":"clxauthor1","title":"Jane Doe","slug":"jane-doe"}]}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/query/schema-types":{"get":{"tags":["Schemas"],"summary":"List schema types","description":"Returns content type definitions (`lucidity_schema_types`) for the API key’s workspace, including field definitions.","operationId":"querySchemaTypes","responses":{"200":{"description":"Schema types for the workspace","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SchemaType"}},"examples":{"default":{"summary":"Default schemas","value":[{"_type":"schema","_id":"clxschema1","name":"post","title":"Post","description":"Blog posts and articles","icon":"article","fields":[{"name":"title","type":"string","title":"Title","required":true},{"name":"slug","type":"slug","title":"Slug","required":true}]}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/query/templates":{"get":{"tags":["Templates"],"summary":"List or get HTML templates","description":"Returns HTML templates for the API key’s workspace.\n\n- Without `name`: list of templates (html omitted)\n- With `name`: full template including `html`","operationId":"queryTemplates","parameters":[{"name":"name","in":"query","required":false,"description":"Template API name (e.g. `blog-post`)","schema":{"type":"string","example":"blog-post"}}],"responses":{"200":{"description":"Template or list of templates","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Template"},{"type":"array","items":{"$ref":"#/components/schemas/TemplateSummary"}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Template not found when `name` is provided"}}}},"/api/render":{"get":{"tags":["Templates"],"summary":"Render a published document as HTML","description":"Renders a published document through a stored Mustache HTML template.\nInject JSON-LD with `{{{jsonLd}}}` in the template head.\n\nUseful placeholders: `{{title}}`, `{{description}}`, `{{pageUrl}}`, `{{publishedDate}}`,\n`{{{body}}}`, `{{#toc}}…{{/toc}}`, `{{#related}}…{{/related}}`, `{{site.name}}`.","operationId":"renderDocument","parameters":[{"name":"template","in":"query","required":true,"description":"Template API name","schema":{"type":"string","example":"blog-post"}},{"name":"slug","in":"query","required":true,"description":"Published document slug","schema":{"type":"string","example":"hello-world"}},{"name":"type","in":"query","required":false,"description":"Schema type API name. Required if the template has no schemaType.","schema":{"type":"string","example":"post"}},{"name":"related","in":"query","required":false,"description":"Number of related documents of the same type (0–12). Default 3.","schema":{"type":"integer","minimum":0,"maximum":12,"default":3}},{"name":"expand","in":"query","required":false,"description":"Expand collection fields. Default `true`.","schema":{"type":"string","enum":["true","false"],"default":"true"}},{"name":"workspace","in":"query","required":false,"description":"Workspace slug, name, or id. Must match the API key’s workspace.","schema":{"type":"string","example":"web-development-sheffield"}}],"responses":{"200":{"description":"Rendered HTML page","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Template or published document not found"}}}}},"components":{"securitySchemes":{"ApiKeyBearer":{"type":"http","scheme":"bearer","bearerFormat":"luc_","description":"API key from Studio → API keys. Example: `Authorization: Bearer luc_…`"},"ApiKeyHeader":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"Same API key via header. Example: `X-Api-Key: luc_…`"}},"responses":{"Unauthorized":{"description":"Missing, invalid, or revoked API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"statusCode":401,"statusMessage":"API key required. Create one in Studio → API keys."}}}}},"schemas":{"HealthResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"service":{"type":"string","example":"lucidity"},"env":{"type":"object","additionalProperties":{"type":"boolean"}},"hint":{"type":"string"}}},"Document":{"type":"object","description":"Published document. Field payload varies by schema type.","properties":{"_type":{"type":"string","description":"Schema type name","example":"post"},"_id":{"type":"string","description":"Document id"},"_publishedAt":{"type":"string","format":"date-time","description":"Present on list responses"}},"additionalProperties":true},"CollectionExpansion":{"type":"array","description":"Expanded collection field: one entry per included schema type.","items":{"type":"object","properties":{"type":{"type":"string","description":"Included schema type name","example":"post"},"relation":{"type":"string","enum":["all","one"]},"documentId":{"type":["string","null"],"description":"Present when relation is `one`"},"items":{"type":"array","items":{"$ref":"#/components/schemas/Document"}}},"required":["type","relation","items"]}},"SchemaType":{"type":"object","properties":{"_type":{"type":"string","enum":["schema"]},"_id":{"type":"string"},"name":{"type":"string","example":"post"},"title":{"type":"string","example":"Post"},"description":{"type":["string","null"]},"icon":{"type":["string","null"]},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FieldDefinition"}}}},"FieldDefinition":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["string","text","html","slug","number","boolean","datetime","image","reference","collection","array","object"]},"title":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean"},"options":{"type":"object","additionalProperties":true},"to":{"type":"array","items":{"type":"string"}},"of":{"type":"array","items":{"$ref":"#/components/schemas/FieldDefinition"}}},"required":["name","type","title"]},"TemplateSummary":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","example":"blog-post"},"title":{"type":"string","example":"Blog post"},"description":{"type":["string","null"]},"schemaType":{"type":["string","null"],"example":"post"},"jsonLdType":{"type":["string","null"],"example":"BlogPosting"},"siteConfig":{"type":"object","additionalProperties":true},"updatedAt":{"type":"string","format":"date-time"}}},"Template":{"allOf":[{"$ref":"#/components/schemas/TemplateSummary"},{"type":"object","properties":{"html":{"type":"string","description":"Mustache HTML template source"}}}]},"Error":{"type":"object","properties":{"statusCode":{"type":"integer"},"statusMessage":{"type":"string"},"message":{"type":"string"}}}}},"security":[{"ApiKeyBearer":[]},{"ApiKeyHeader":[]}]}