{"openapi":"3.1.0","info":{"title":"StaticOwl CMS API","version":"1.0.0","description":"StaticOwl CMS — the customer integration surface. Internal/operator endpoints are not included.","contact":{"name":"StaticOwl","url":"https://staticowl.com/docs/"}},"servers":[{"url":"https://cms.staticowl.com","description":"Production (canonical)"},{"url":"https://app.staticowl.com","description":"Production (alias, historical)"}],"security":[{"BearerApiKey":[]}],"paths":{"/api/health":{"get":{"summary":"Liveness probe.","description":"Dumb 200-always endpoint suitable as a load-balancer target. Makes no engine call, so it never fails on downstream outage. For engine reachability use /api/health/detail.","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"platformGraph":{"type":"string"}}}}}}}}},"/api/health/detail":{"get":{"summary":"Deep-probe health (engine reachability + cache snapshot).","description":"Probes the engine endpoint with a 3s timeout and returns whether it is reachable, http status, probe duration, plus the apiKeyCache snapshot. Always 200 (with degraded:true when the engine is unreachable) so this can be polled without flapping. Use for ops dashboards + alerting.","security":[],"responses":{"200":{"description":"Status payload","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"]},"degraded":{"type":"boolean"},"cms":{"type":"string"},"engine":{"type":"object","properties":{"endpoint":{"type":"string"},"reachable":{"type":"boolean"},"httpStatus":{"type":"integer","nullable":true},"probeMs":{"type":"integer","nullable":true},"error":{"type":"string","nullable":true}}},"apiKeyCache":{"type":"object","properties":{"hits":{"type":"integer"},"misses":{"type":"integer"},"evictions":{"type":"integer"},"invalidations":{"type":"integer"},"staleServed":{"type":"integer"},"size":{"type":"integer"}}},"probedAt":{"type":"string","format":"date-time"},"durationMs":{"type":"integer"}}}}}}}}},"/api/maintenance/embeddings/materialize":{"post":{"summary":"Materialize structural-fingerprint embeddings for a content type.","description":"Calls `db.materializeEmbeddings(<nodeLabel>)` against the site graph for the named type. Idempotent — already-embedded nodes are untouched. Run after a bulk import if `{% similar %}` returns empty results.","parameters":[{"name":"type","in":"query","schema":{"type":"string"},"description":"Content type name (e.g. `lesson`)."}],"responses":{"200":{"description":"OK — embeddings materialized","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"type":{"type":"string"},"nodeLabel":{"type":"string"},"durationMs":{"type":"integer"},"note":{"type":"string"}}}}}},"400":{"description":"type parameter missing"},"404":{"description":"type not found on this site"},"502":{"description":"Engine call failed (procedure missing, label empty, etc.) — body includes a hint string","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"},"type":{"type":"string"},"nodeLabel":{"type":"string"}}}}}}}}},"/api/maintenance/engine/procedures":{"get":{"summary":"List engine procedures (diagnostic).","description":"Probes the engine for available procedures via SHOW PROCEDURES → dbms.procedures() → db.procedures(), returning whichever shape comes back first. Use this when \"procedure not found\" errors are confusing — it tells you definitively what the engine exposes.","responses":{"200":{"description":"OK — at least one probe succeeded","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"via":{"type":"string","description":"Which probe query succeeded."},"procedureCount":{"type":"integer"},"procedures":{"type":"array","items":{"type":"object"}},"attempts":{"type":"array","items":{"type":"object"}}}}}}},"502":{"description":"Engine does not respond to any known introspection query — body includes the full attempt log","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"error":{"type":"string"},"hint":{"type":"string"},"attempts":{"type":"array","items":{"type":"object"}}}}}}}}}},"/api/sites":{"get":{"summary":"List sites the caller has access to.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Site"}}}}}}},"post":{"summary":"Create a site.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"publicUrl":{"type":"string"},"description":{"type":"string"},"mantra":{"type":"string"},"theme":{"type":"object","properties":{"primary":{"type":"string"},"accent":{"type":"string"}}},"starterKit":{"type":"string","enum":["designer","developer","author","none"]}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"500":{"description":"partial_site_creation — platform service returned an incomplete record. Retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/sites/{id}":{"get":{"summary":"Get a site.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}}}},"put":{"summary":"Update site settings. Partial; pass only changing fields.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}}}}},"/api/types":{"get":{"summary":"List content types.","description":"Returns the summary shape (`ContentTypeSummary`) — includes `emitPages`, `routePattern`, `fields: [{name, type}]` so authors can self-verify their schema without running a build. For richer per-field info (defaults, minLength, allowed values) call GET /api/types/:name.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ContentTypeSummary"}}}}}}},"post":{"summary":"Create a content type.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentType"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentType"}}}}}}},"/api/types/{name}":{"get":{"summary":"Get a content type.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentType"}}}}}},"patch":{"summary":"Update a content type. Partial.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentType"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentType"}}}}}}},"/api/content/{type}":{"get":{"summary":"List content items. Pass ?withMeta=1 for total + pagination metadata.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"type","in":"path","required":true,"schema":{"type":"string"}},{"name":"withMeta","in":"query","schema":{"type":"string","enum":["1","true"]},"description":"Return envelope shape {items,total,limit,offset,appliedFilters} instead of bare array."},{"name":"limit","in":"query","schema":{"type":"integer"}},{"name":"offset","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentList"}}}}}},"post":{"summary":"Create a content item.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"type","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentRecord"}}}}}}},"/api/content/{type}/{id}":{"get":{"summary":"Get a content item.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"type","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentRecord"}}}},"404":{"description":"Not found"}}},"patch":{"summary":"Update a content item. Partial.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"type","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentRecord"}}}}}},"delete":{"summary":"Delete a content item.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"type","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"404":{"description":"Not found"}}}},"/api/import/json":{"post":{"summary":"Bulk-load content records. One UNWIND query per chunk; ~100 rec/s through the gateway.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["typeName","records"],"properties":{"typeName":{"type":"string"},"records":{"type":"array","items":{"type":"object","additionalProperties":true}},"batchSize":{"type":"integer","default":100,"minimum":1,"maximum":500},"skipExisting":{"type":"boolean","default":false},"dryRun":{"type":"boolean","default":false}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJsonResponse"}}}},"413":{"description":"Too many records. Response carries suggestedBatchSize + suggestedCallCount.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/import/edges":{"post":{"summary":"Bulk-MERGE relationship edges between existing nodes.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["edges"],"properties":{"edges":{"type":"array","items":{"type":"object","required":["from","edge","to"],"properties":{"from":{"type":"string"},"edge":{"type":"string","description":"Uppercase identifier."},"to":{"type":"string"},"props":{"type":"object","additionalProperties":true}}}},"batchSize":{"type":"integer","default":500,"minimum":1,"maximum":2000}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportEdgesResponse"}}}}}}},"/api/templates":{"get":{"summary":"List templates.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Template"}}}}}}},"post":{"summary":"Create a template. Blocks up to ~3s waiting for read-after-write visibility; returns 502 template_write_not_visible if the read view never sees the write (KT #408, 2026-07-02).","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"content":{"type":"string"},"html":{"type":"string","description":"Alias for content. Either works."},"boundType":{"type":"string"}}}}}},"responses":{"201":{"description":"Created — write persisted AND visible on the read side.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"400":{"description":"Bad request (validation, missing body)."},"502":{"description":"template_write_not_visible — write ack'd but read view never reflected it within ~3s. Retry the request (idempotent).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["template_write_not_visible"]}}}}}}}}},"/api/templates/{name}":{"get":{"summary":"Get a template at its head version. Returns the same shape as list() entries plus content + version.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}}}},"put":{"summary":"Update a template (creates a new version). Same read-after-write consistency + 502 semantics as POST.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string"},"html":{"type":"string"},"boundType":{"type":"string"}}}}}},"responses":{"200":{"description":"OK — write persisted AND visible.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"400":{"description":"Bad request."},"502":{"description":"template_write_not_visible — retry.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["template_write_not_visible"]}}}}}}}}},"/api/build":{"post":{"summary":"Trigger a build + deploy. Response includes serveCheck against the published URL.","description":"Modes:\n  - `full` — rebuild every page.\n  - `incremental` — rebuild only impacted artifacts (default).\n  - `auto` — pick full vs incremental based on change surface.\n  - `validate` — dry-run: compile in memory, return artifact list + transformErrors, DO NOT deploy or record a BuildEvent. Fast way to smoke a template edit before spending a real build slot.\n\nAsync mode: set `async: true` (or `?async=1`) to return 202 + `statusUrl` immediately and run the build in the background. Poll GET /api/build/status/:buildId for completion. Sync mode is the default.\n\nenv=prod requires content published to prod first (content_publish); otherwise the compile emits a stub because there are no envPublishedAt_prod stamps to walk. env=dev builds all unstamped content and deploys to the flat-layout bucket for sites with a customDomain.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"env","in":"query","schema":{"type":"string"},"description":"Overrides the env in the request body when both are present."},{"name":"async","in":"query","schema":{"type":"string","enum":["0","1"]},"description":"Alternative to body.async — set to 1 for 202 + statusUrl."},{"name":"dryRun","in":"query","schema":{"type":"string","enum":["0","1"]},"description":"Alternative to mode:\"validate\". When 1, treated the same as validate."}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"env":{"type":"string","example":"prod"},"mode":{"type":"string","enum":["full","incremental","auto","validate"]},"async":{"type":"boolean"},"outputChannels":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildResult"}}}},"202":{"description":"Async — build kicked off in background. Poll `statusUrl`.","content":{"application/json":{"schema":{"type":"object","required":["ok","buildId","envId","status","statusUrl"],"properties":{"ok":{"type":"boolean"},"buildId":{"type":"string"},"envId":{"type":"string"},"status":{"type":"string","enum":["running"]},"statusUrl":{"type":"string"}}}}}}}}},"/api/build/status/{buildId}":{"get":{"summary":"Poll the status of an async build kicked off via POST /api/build with async:true.","parameters":[{"name":"X-Site-Id","in":"header","required":true,"schema":{"type":"string","example":"site:my-site"},"description":"Site scope. Required on every per-site endpoint (most of /api/*)."},{"name":"buildId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK — includes final artifact/deploy counts once status flips from `running` to `ok`/`error`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildResult"}}}}}}},"/api/audit/verify":{"get":{"summary":"Walk the engine's audit hash chain for the site's graph. Returns 200 whether chain is ok OR broken; the status field carries the verdict.","parameters":[{"name":"graph","in":"query","required":true,"schema":{"type":"string"},"description":"Graph name (see Site.graphName)."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditStatus"}}}},"404":{"description":"Graph not found / no access."}}}},"/api/audit/checklist":{"post":{"summary":"Run db.compliance.checklist() against the site's graph.","parameters":[{"name":"graph","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"feature":{"type":"string"},"status":{"type":"string"},"version":{"type":"string","nullable":true},"last_verified_at":{"type":"string","nullable":true},"message":{"type":"string","nullable":true}}}}}}}}}}}},"/api/metrics/apikey-cache":{"get":{"summary":"resolveApiKey cache stats — A1 / 2026-06-17. Unprotected.","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"hits":{"type":"integer"},"misses":{"type":"integer"},"evictions":{"type":"integer"},"invalidations":{"type":"integer"},"size":{"type":"integer"}}}}}}}}}},"components":{"schemas":{"Site":{"type":"object","required":["id","name","slug","graphName","createdAt"],"properties":{"id":{"type":"string","example":"site:my-site"},"name":{"type":"string"},"slug":{"type":"string"},"graphName":{"type":"string","description":"Engine-side graph name. Immutable per-site (uuid) for sites created 2026-06-14+."},"publicUrl":{"type":"string","nullable":true},"description":{"type":"string"},"mantra":{"type":"string"},"theme":{"type":"object","properties":{"primary":{"type":"string"},"accent":{"type":"string"},"font":{"type":"string","nullable":true}}},"searchEmission":{"type":"string","enum":["none","bm25","bm25+vector"],"description":"Build-time search index emission. \"none\" turns off the StaticOwl search index for sites running their own pipeline."},"createdAt":{"type":"string","format":"date-time"}}},"ContentType":{"type":"object","required":["name","label","nodeLabel","keyField","fields"],"properties":{"name":{"type":"string","example":"blog_post"},"label":{"type":"string","example":"Blog Post"},"nodeLabel":{"type":"string","example":"BlogPost"},"keyField":{"type":"string","example":"slug"},"fields":{"type":"array","items":{"type":"object","required":["name","type"],"properties":{"name":{"type":"string"},"type":{"type":"string","enum":["string","text","markdown","richtext","number","boolean","date","datetime","enum","stringlist","image","reference"]},"required":{"type":"boolean"},"unique":{"type":"boolean"},"default":{},"values":{"type":"array","items":{"type":"string"},"description":"Allowed values for enum fields."},"minLength":{"type":"integer"},"maxLength":{"type":"integer"},"regex":{"type":"string"}}}},"relationships":{"type":"array","items":{"type":"object","required":["name","edgeLabel","target"],"properties":{"name":{"type":"string"},"edgeLabel":{"type":"string","description":"MUST be uppercase identifier."},"target":{"type":"string","description":"Content type name of the relationship target."},"kind":{"type":"string","enum":["one-to-one","one-to-many","many-to-one","many-to-many"]},"required":{"type":"boolean"}}}},"routePattern":{"type":"string","nullable":true,"description":"Per-type URL pattern. Supports {slug}, {<fieldName>}, {parent.slug}, {parent.<fieldName>}, literals. Example: \"/{parent.slug}/{filename}.html\"."},"emitPages":{"type":"boolean","description":"When false, build pipeline does NOT emit per-item HTML pages. Items stay queryable via {% list %} / {% traverse %}. Defaults to true."},"listingOnly":{"type":"boolean","description":"Alias for `emitPages: false`. Setting either to the equivalent value gives the same result; explicit `emitPages` wins if both are set. Added 2026-07-09 for tester discoverability — no per-item detail pages, items only appear via `{% list %}`. Use for subscription tiers, FAQ entries, footer nav-groups."}}},"ContentTypeSummary":{"type":"object","required":["name","label","nodeLabel","fieldCount","relationshipCount","allowedChildren","emitPages","routePattern","fields"],"description":"Summary shape returned by GET /api/types. Extended 2026-07-10 to expose emitPages + routePattern + per-field type map so authors can self-verify without a full build.","properties":{"name":{"type":"string","example":"plan"},"label":{"type":"string","example":"Plan"},"nodeLabel":{"type":"string","example":"Plan"},"fieldCount":{"type":"integer"},"relationshipCount":{"type":"integer"},"allowedChildren":{"type":"array","items":{"type":"string"}},"icon":{"type":"string","nullable":true},"emitPages":{"type":"boolean","description":"Whether the build emits per-item detail pages. Legacy rows without this field default to `true`."},"routePattern":{"type":"string","nullable":true,"description":"URL template for detail pages; null when the legacy `/<type>/<slug>/` fallback applies."},"fields":{"type":"array","description":"Compact per-field type map. Anything richer (default, values, minLength, etc.) lives on GET /api/types/:name.","items":{"type":"object","required":["name","type"],"properties":{"name":{"type":"string"},"type":{"type":"string"}}}}}},"ContentRecord":{"type":"object","required":["id","type","fields"],"properties":{"id":{"type":"string"},"type":{"type":"string"},"fields":{"type":"object","additionalProperties":true},"relationships":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"ContentList":{"oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/ContentRecord"},"description":"Default shape."},{"type":"object","description":"Returned when ?withMeta=1 is set.","required":["items","total"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContentRecord"}},"total":{"type":"integer","description":"Total matching the where clause (independent of limit)."},"limit":{"type":"integer","nullable":true},"offset":{"type":"integer"},"appliedFilters":{"type":"object","additionalProperties":true}}}]},"Template":{"type":"object","required":["name","content","version"],"properties":{"name":{"type":"string"},"content":{"type":"string","description":"Liquid source."},"version":{"type":"integer"},"latestVersion":{"type":"integer"},"boundType":{"type":"string","description":"Content type the template is bound to, or \"\" for wildcard."},"enabled":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"}}},"BuildResult":{"type":"object","properties":{"ok":{"type":"boolean"},"buildId":{"type":"string"},"mode":{"type":"string","enum":["full","incremental","auto","manual"]},"artifacts":{"type":"integer"},"durationMs":{"type":"integer"},"transformErrors":{"type":"array","items":{"type":"object","properties":{"contentId":{"type":"string"},"transform":{"type":"string"},"error":{"type":"string"}}}},"deploy":{"type":"object","properties":{"ok":{"type":"boolean"},"bucket":{"type":"string"},"keyPrefix":{"type":"string"},"publicUrl":{"type":"string","nullable":true},"filesUploaded":{"type":"integer"},"filesDeleted":{"type":"integer"},"bytes":{"type":"integer"},"durationMs":{"type":"integer"},"serveCheck":{"type":"object","nullable":true,"description":"Post-deploy probe against publicUrl. Added 2026-06-18.","properties":{"ok":{"type":"boolean"},"status":{"type":"integer"},"durationMs":{"type":"integer"},"url":{"type":"string"},"error":{"type":"string"}}}}}}},"AuditStatus":{"type":"object","required":["graph","status","records","last_seq"],"properties":{"graph":{"type":"string"},"status":{"type":"string","enum":["ok","broken","empty"]},"records":{"type":"integer"},"last_seq":{"type":"integer"},"last_hash":{"type":"string"},"verified_at":{"type":"string","format":"date-time"},"reason":{"type":"string","nullable":true}}},"ImportJsonResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"totalRecords":{"type":"integer"},"created":{"type":"integer"},"failed":{"type":"integer"},"batches":{"type":"integer"},"tookMs":{"type":"integer"},"recordsPerSec":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"id":{"type":"string"},"error":{"type":"string"}}}},"gatewayTimeoutWarning":{"type":"object","nullable":true,"properties":{"message":{"type":"string"},"suggestedBatchSize":{"type":"integer"},"suggestedCallCount":{"type":"integer"}}}}},"ImportEdgesResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"totalEdges":{"type":"integer"},"merged":{"type":"integer"},"mismatch":{"type":"integer","description":"Triples whose from/to endpoint didn't exist (silent no-op)."},"failed":{"type":"integer"},"batches":{"type":"integer"},"tookMs":{"type":"integer"},"edgesPerSec":{"type":"number"},"errors":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"error":{"type":"string"}}}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}},"securitySchemes":{"BearerApiKey":{"type":"http","scheme":"bearer","description":"CMS-issued (`so_...`) or engine-direct (`gq_...`) API key. CMS-issued keys with `allowedSites` are recommended for production SDK use."}}}}