{"openapi":"3.0.0","info":{"title":"Centrali Pages Service API","version":"5.1.2","description":"API documentation for the Centrali Pages Service - Hosted pages runtime for internal tools, portals, forms, dashboards, and workflow apps.","contact":{"name":"Centrali Support"}},"servers":[{"url":"https://api.centrali.io/pages/ws/{workspaceSlug}/api/v1/pages","description":"Workspace-scoped API","variables":{"workspaceSlug":{"default":"demo","description":"The workspace slug"}}}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token obtained from the IAM service"}},"schemas":{"Error":{"type":"object","required":["error","message","status","timestamp","path"],"properties":{"error":{"type":"string","example":"VALIDATION_ERROR","description":"Machine-readable error code"},"message":{"type":"string","example":"name is required","description":"Human-readable error message"},"status":{"type":"integer","example":400,"description":"HTTP status code"},"timestamp":{"type":"string","format":"date-time"},"path":{"type":"string","example":"/ws/my-ws/api/v1/pages"},"requestId":{"type":"string","description":"Request ID for tracing"}}},"PaginatedResponse":{"type":"object","properties":{"data":{"type":"array","items":{}},"meta":{"type":"object","properties":{"total":{"type":"integer","example":100},"page":{"type":"integer","example":1},"pageSize":{"type":"integer","example":20}}}}},"Page":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspaceSlug":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"pageType":{"type":"string","enum":["list","detail","form","dashboard"]},"createdBy":{"type":"string","format":"uuid"},"updatedBy":{"type":"string","format":"uuid"},"isDeleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"PageVersion":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"pageId":{"type":"string","format":"uuid"},"workspaceSlug":{"type":"string"},"versionNumber":{"type":"integer"},"state":{"type":"string","enum":["draft","published","superseded"]},"definition":{"type":"object","description":"Page definition JSONB"},"artifactManifest":{"type":"array","items":{"type":"object"}},"changeSummary":{"type":"string","nullable":true},"createdBy":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"}}},"PagePublication":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"pageId":{"type":"string","format":"uuid"},"workspaceSlug":{"type":"string"},"versionId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["active","inactive"]},"publishedBy":{"type":"string","format":"uuid"},"publishedAt":{"type":"string","format":"date-time"},"unpublishedAt":{"type":"string","format":"date-time","nullable":true}}}}},"tags":[{"name":"Pages","description":"Page CRUD operations"},{"name":"Page Versions","description":"Page version and draft management"},{"name":"Publishing","description":"Page publishing and unpublishing"},{"name":"Access Policy","description":"Page access control configuration"},{"name":"Theme","description":"Workspace theme management"},{"name":"Assembly","description":"Guided page generation"},{"name":"Runtime","description":"Runtime page resolution and actions"}],"security":[{"bearerAuth":[]}],"paths":{"/{pageId}/access-policy":{"put":{"summary":"Set access policy","description":"Create or update the access policy for a page. Controls whether the page is public, requires authentication, or requires specific roles.","tags":["Access Policy"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accessMode"],"properties":{"accessMode":{"type":"string","enum":["public","authenticated","role-gated"],"description":"Access control mode"},"requiredRoles":{"type":"array","items":{"type":"string"},"default":[],"description":"Required roles (only used when accessMode is role-gated)"}}}}}},"responses":{"200":{"description":"Access policy saved","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"pageId":{"type":"string","format":"uuid"},"workspaceSlug":{"type":"string"},"accessMode":{"type":"string","enum":["public","authenticated","role-gated"]},"requiredRoles":{"type":"array","items":{"type":"string"}},"updatedBy":{"type":"string","format":"uuid"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Page not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{ws}/actions":{"post":{"summary":"Execute a page action","description":"Execute a page action (create record, update record, run query, execute function, start orchestration, or enqueue job). Requires authentication via bearer token.","tags":["Runtime"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"ws","required":true,"schema":{"type":"string"},"description":"Workspace slug"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pageId","actionId","type","targetRef"],"properties":{"pageId":{"type":"string","format":"uuid"},"actionId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["create-record","update-record","run-query","execute-function","start-orchestration","enqueue-job"]},"targetRef":{"type":"string","description":"Target resource reference (structure ID, query ID, etc.)"},"payload":{"type":"object","default":{},"description":"Action-specific payload"}}}}}},"responses":{"200":{"description":"Action executed synchronously","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"completed"},"result":{"type":"object"}}}}}},"202":{"description":"Action accepted for async execution","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"accepted"},"trackingId":{"type":"string"}}}}}},"400":{"description":"Validation error or unknown action type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{ws}/actions/{actionId}/status":{"get":{"summary":"Check action status","description":"Check the execution status of a previously submitted action. Status tracking is not yet fully implemented.","tags":["Runtime"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"ws","required":true,"schema":{"type":"string"},"description":"Workspace slug"},{"in":"path","name":"actionId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Action ID"}],"responses":{"200":{"description":"Action status","content":{"application/json":{"schema":{"type":"object","properties":{"actionId":{"type":"string","format":"uuid"},"status":{"type":"string"},"message":{"type":"string"}}}}}}}}},"/generate-starter-pages":{"post":{"summary":"Generate starter page proposals","description":"Given a set of structure IDs and optional goals, generate page definition proposals using guided assembly.","tags":["Assembly"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["structureIds"],"properties":{"structureIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"description":"Structure IDs to generate pages for"},"goals":{"type":"array","items":{"type":"string"},"default":[],"description":"Optional goals to guide page generation"}}}}}},"responses":{"200":{"description":"Page proposals generated","content":{"application/json":{"schema":{"type":"object","properties":{"proposals":{"type":"array","items":{"type":"object"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/accept-proposal":{"post":{"summary":"Accept a page proposal","description":"Accept a generated page proposal and create the page with its initial draft definition.","tags":["Assembly"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","slug","pageType","definition","generationSource"],"properties":{"name":{"type":"string","example":"Customer List"},"slug":{"type":"string","example":"customer-list"},"pageType":{"type":"string","enum":["list","detail","form","dashboard"]},"definition":{"type":"object","description":"The page definition to use as the initial draft"},"generationSource":{"type":"string","description":"The source that generated this proposal"}}}}}},"responses":{"201":{"description":"Page created from proposal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slug conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/modify-definition":{"post":{"summary":"Modify an existing page definition with AI","description":"Send the current page definition and modification instructions to AI. Returns a jobId for polling.","tags":["Assembly"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pageSlug","pageType","currentDefinition","instructions"],"properties":{"pageSlug":{"type":"string","description":"Slug of the page being modified"},"pageType":{"type":"string","enum":["list","detail","form","dashboard"]},"currentDefinition":{"type":"object","description":"The current page definition JSON"},"instructions":{"type":"string","description":"Natural language instructions for how to modify the page"}}}}}},"responses":{"202":{"description":"Modification job started","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string"}}}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"}}}},"/assembly/jobs/{jobId}":{"get":{"summary":"Poll AI assembly job status","description":"Check the status of an AI-powered page assembly job. Poll until status is 'completed' or 'failed'.","tags":["Assembly"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"jobId","required":true,"schema":{"type":"string"},"description":"Job ID returned from POST /generate-starter-pages with mode 'ai'"}],"responses":{"200":{"description":"Job status","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","processing","completed","failed"]},"result":{"type":"object"},"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized"},"500":{"description":"Internal server error"}}}},"/navigation":{"get":{"summary":"Get workspace navigation config","description":"Retrieve the navigation configuration for the workspace, enriched with page metadata.","tags":["Navigation"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Navigation configuration","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspaceSlug":{"type":"string"},"config":{"type":"object"},"updatedBy":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"No navigation config found"}}},"put":{"summary":"Set workspace navigation config","description":"Create or update the workspace navigation configuration.","tags":["Navigation"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["config"],"properties":{"config":{"type":"object","required":["enabled","branding","items"],"properties":{"enabled":{"type":"boolean"},"branding":{"type":"object","properties":{"logoUrl":{"type":"string","nullable":true},"displayName":{"type":"string","nullable":true}}},"items":{"type":"array","items":{"type":"object"}}}}}}}}},"responses":{"200":{"description":"Navigation config saved"},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"}}},"delete":{"summary":"Delete workspace navigation config","description":"Remove the workspace navigation configuration. Pages revert to standalone rendering.","tags":["Navigation"],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Navigation config deleted"},"401":{"description":"Unauthorized"},"404":{"description":"No navigation config found"}}}},"/":{"get":{"summary":"List pages","description":"Retrieve a paginated list of pages in the workspace.","tags":["Pages"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"query","name":"page","schema":{"type":"integer","default":1},"description":"Page number"},{"in":"query","name":"pageSize","schema":{"type":"integer","default":20},"description":"Number of items per page"},{"in":"query","name":"pageType","schema":{"type":"string","enum":["list","detail","form","dashboard"]},"description":"Filter by page type"}],"responses":{"200":{"description":"Paginated list of pages","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a page","description":"Create a new page in the workspace.","tags":["Pages"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","slug","pageType"],"properties":{"name":{"type":"string","example":"Customer List"},"slug":{"type":"string","example":"customer-list"},"pageType":{"type":"string","enum":["list","detail","form","dashboard"]},"description":{"type":"string","nullable":true}}}}}},"responses":{"201":{"description":"Page created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slug conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{pageId}":{"get":{"summary":"Get a page by ID","description":"Retrieve a single page by its ID.","tags":["Pages"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"}],"responses":{"200":{"description":"Page found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Page not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Update a page","description":"Partially update a page's metadata (name, slug, description).","tags":["Pages"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string"}}}}}},"responses":{"200":{"description":"Page updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Page not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slug conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Delete a page","description":"Soft-delete a page by its ID.","tags":["Pages"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"}],"responses":{"204":{"description":"Page deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Page not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{pageId}/validate":{"post":{"summary":"Validate a page for publishing","description":"Run validation checks on a page's current draft to determine if it is ready to publish.","tags":["Publishing"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"}],"responses":{"200":{"description":"Validation result","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"issues":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["error","warning"]},"targetType":{"type":"string","enum":["block","section"]},"targetId":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"},"remediation":{"type":"string"}}}}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Page or draft not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{pageId}/publish":{"post":{"summary":"Publish a page","description":"Publish the current draft of a page. Validates the draft first and rejects with 422 if errors exist.","tags":["Publishing"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"}],"responses":{"200":{"description":"Page published successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PagePublication"},{"type":"object","properties":{"runtimeUrl":{"type":"string","description":"Public URL for the published page"}}}]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Page or draft not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed — page has errors that prevent publishing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{pageId}/unpublish":{"post":{"summary":"Unpublish a page","description":"Deactivate the active publication for a page, making it no longer accessible at its runtime URL.","tags":["Publishing"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"}],"responses":{"200":{"description":"Page unpublished","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagePublication"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No active publication found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/callback":{"get":{"summary":"OIDC callback","description":"Exchanges an authorization code for tokens via the IAM service and returns an HTML page that stores the tokens in sessionStorage before redirecting to the original page.","tags":["Runtime"],"security":[],"parameters":[{"in":"query","name":"code","required":true,"schema":{"type":"string"},"description":"Authorization code from the OIDC provider"},{"in":"query","name":"state","schema":{"type":"string"},"description":"Original page path to redirect to after authentication"}],"responses":{"200":{"description":"HTML page that stores tokens and redirects","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"Missing code parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Token exchange failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{ws}/{pageSlug}":{"get":{"summary":"Resolve a published page","description":"Resolve a published page by workspace slug and page slug. Enforces access policy (public, authenticated, or role-gated). Returns the page definition, version info, access mode, and workspace theme.","tags":["Runtime"],"parameters":[{"in":"path","name":"ws","required":true,"schema":{"type":"string"},"description":"Workspace slug"},{"in":"path","name":"pageSlug","required":true,"schema":{"type":"string"},"description":"Page slug"},{"in":"header","name":"Authorization","schema":{"type":"string"},"description":"Bearer token (required for authenticated and role-gated pages)"}],"responses":{"200":{"description":"Resolved page with definition, version, access mode, and theme","content":{"application/json":{"schema":{"type":"object","properties":{"page":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"pageType":{"type":"string","enum":["list","detail","form","dashboard"]}}},"version":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"versionNumber":{"type":"integer"},"definition":{"type":"object"}}},"accessMode":{"type":"string","enum":["public","authenticated","role-gated"]},"theme":{"type":"object","nullable":true}}}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"loginUrl":{"type":"string","description":"URL to redirect the user to for authentication"}}}]}}}},"403":{"description":"Insufficient role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Page not found or not published","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Workspace unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{ws}/resolve-data":{"post":{"summary":"Resolve data for page blocks","description":"Resolve data sources for one or more page blocks. Each data source is resolved independently via NATS and results are keyed by blockId.","tags":["Runtime"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"ws","required":true,"schema":{"type":"string"},"description":"Workspace slug"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["dataSources"],"properties":{"dataSources":{"type":"array","items":{"type":"object","required":["blockId","type","ref"],"properties":{"blockId":{"type":"string"},"type":{"type":"string","enum":["structure","query"]},"ref":{"type":"string","description":"Structure ID or query ID"},"config":{"type":"object","description":"Optional data source configuration (fields, filters, sort, limit)"}}}}}}}}},"responses":{"200":{"description":"Resolved data keyed by blockId","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/theme":{"get":{"summary":"Get workspace theme","description":"Retrieve the current theme configuration for the workspace.","tags":["Theme"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Theme configuration","content":{"application/json":{"schema":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"workspaceSlug":{"type":"string"},"config":{"type":"object","properties":{"primaryColor":{"type":"string","example":"#1a73e8"},"accentColor":{"type":"string","example":"#ff6d00"},"logoUrl":{"type":"string","nullable":true},"fontFamily":{"type":"string","nullable":true}}},"updatedBy":{"type":"string","format":"uuid"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Set workspace theme","description":"Create or update the workspace theme configuration used by published pages.","tags":["Theme"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["config"],"properties":{"config":{"type":"object","required":["primaryColor","accentColor"],"properties":{"primaryColor":{"type":"string","example":"#1a73e8"},"accentColor":{"type":"string","example":"#ff6d00"},"logoUrl":{"type":"string","nullable":true},"fontFamily":{"type":"string","nullable":true}}}}}}}},"responses":{"200":{"description":"Theme saved","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspaceSlug":{"type":"string"},"config":{"type":"object"},"updatedBy":{"type":"string","format":"uuid"},"updatedAt":{"type":"string","format":"date-time"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{pageId}/draft":{"put":{"summary":"Save a draft","description":"Create or update the draft version of a page's definition.","tags":["Page Versions"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["definition"],"properties":{"definition":{"type":"object","description":"The page definition JSONB"},"changeSummary":{"type":"string","nullable":true,"description":"Summary of changes in this draft"}}}}}},"responses":{"200":{"description":"Draft saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageVersion"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"Get the current draft","description":"Retrieve the current draft version of a page.","tags":["Page Versions"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"}],"responses":{"200":{"description":"Draft found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageVersion"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Draft not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{pageId}/versions":{"get":{"summary":"List versions","description":"Retrieve a paginated list of versions for a page.","tags":["Page Versions"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"},"description":"Page ID"},{"in":"query","name":"page","schema":{"type":"integer","default":1},"description":"Page number"},{"in":"query","name":"pageSize","schema":{"type":"integer","default":20},"description":"Number of items per page"}],"responses":{"200":{"description":"Paginated list of versions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/{pageId}/generation-context":{"get":{"summary":"Get AI generation context","description":"Returns the AI conversation context from the most recent AI-generated version of a page. Used to resume AI assembly conversations.","tags":["Page Versions"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Generation context found"},"404":{"description":"No AI generation context for this page"}}}},"/{pageId}/versions/{versionId}/restore":{"post":{"summary":"Restore a previous version","description":"Creates a new draft from a previous version's definition. The current published version stays live until the restored draft is published.","tags":["Page Versions"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"versionId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Version restored as new draft"},"404":{"description":"Page or version not found"},"409":{"description":"A draft already exists — publish or discard it first"}}}},"/{pageId}/versions/{versionId}/diff":{"get":{"summary":"Compare two versions","description":"Returns a human-readable diff between two page versions showing added/removed/modified sections, blocks, and actions.","tags":["Page Versions"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"pageId","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"versionId","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"compareWith","required":true,"schema":{"type":"string","format":"uuid"},"description":"Version ID to compare against"}],"responses":{"200":{"description":"Version diff"},"400":{"description":"Missing compareWith parameter"},"404":{"description":"Version not found"}}}}}}