openapi: 3.0.3 info: title: FlatRender Content Service (internal) version: 1.0.0 description: | Templates (project_containers + projects), scenes, content elements, fonts, music, categories, tags, CMS (blogs, slides), SVG previews. servers: - url: http://content-svc.internal/v1 security: - BearerAuth: [] - ServiceToken: [] tags: - name: Templates # project_containers + projects (catalog view) - name: Projects # internal project (AE) management - name: Scenes - name: Elements # content/color/character/shared layers - name: Presets # preset_stories + color presets + character presets - name: Repeaters - name: Fonts - name: Music - name: Categories - name: Tags - name: SVGPreview - name: Favorites - name: CMS - name: ProjectServers paths: # ===================== TEMPLATES (catalog) ===================== /templates: get: tags: [Templates] summary: Browse published templates parameters: - { name: q, in: query, schema: { type: string } } - { name: category_id, in: query, schema: { type: string, format: uuid } } - { name: tag_ids, in: query, schema: { type: array, items: { type: string, format: uuid } }, style: form, explode: true } - { name: mode, in: query, schema: { type: string } } - { name: aspect, in: query, schema: { type: string, enum: ['16:9','9:16','1:1','4:5'] } } - { name: is_premium, in: query, schema: { type: boolean } } - { name: sort, in: query, schema: { type: string, enum: [newest, popular, rate, az] } } - { name: page, in: query, schema: { type: integer, default: 1 } } - { name: page_size, in: query, schema: { type: integer, default: 20 } } responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/TemplateCard' } } meta: { $ref: '#/components/schemas/PaginationMeta' } /templates/{slug_or_id}: get: tags: [Templates] summary: Template detail (container + its projects) parameters: - { name: slug_or_id, in: path, required: true, schema: { type: string } } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/TemplateDetail' } '404': { description: Not found } /templates/{container_id}/projects: get: tags: [Templates] parameters: - { name: container_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/ProjectSummary' } } /templates/{container_id}/related: get: tags: [Templates] summary: Related templates (by category/tag) parameters: - { name: container_id, in: path, required: true, schema: { type: string, format: uuid } } - { name: limit, in: query, schema: { type: integer, default: 8 } } responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/TemplateCard' } } # ===================== Containers (admin CRUD) ===================== /containers: post: tags: [Templates] summary: (Admin) Create container requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ContainerCreate' } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/Container' } /containers/{container_id}: patch: tags: [Templates] summary: (Admin) Update container parameters: - { name: container_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ContainerUpdate' } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/Container' } delete: tags: [Templates] parameters: - { name: container_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '204': { description: Deleted (soft) } /containers/{container_id}/publish: post: tags: [Templates] summary: Publish a container parameters: - { name: container_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '204': { description: Published } /containers/{container_id}/unpublish: post: tags: [Templates] parameters: - { name: container_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: content: application/json: schema: type: object properties: reason: { type: string } responses: '204': { description: Unpublished } # ===================== Projects (admin) ===================== /projects: post: tags: [Projects] summary: (Admin) Create project variant requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ProjectCreate' } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/Project' } /projects/{project_id}: get: tags: [Projects] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/Project' } patch: tags: [Projects] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ProjectUpdate' } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/Project' } delete: tags: [Projects] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '204': { description: Deleted } /projects/{project_id}/full: get: tags: [Projects] summary: Full project graph for editor (scenes + elements + presets) parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/ProjectFull' } /projects/{project_id}/aep: put: tags: [Projects] summary: (Admin) Upload AEP file (multipart) -> stored in MinIO parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: multipart/form-data: schema: type: object properties: file: { type: string, format: binary } responses: '200': content: application/json: schema: type: object properties: aep_file_url: { type: string } aep_file_md5: { type: string } aep_file_size_bytes: { type: integer, format: int64 } /projects/{project_id}/aep/start-upload: post: tags: [Projects] summary: Start multipart upload of large AEP (300MB-3GB) parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: type: object required: [filename, total_size_bytes] properties: filename: { type: string } total_size_bytes: { type: integer, format: int64 } responses: '201': content: application/json: schema: type: object properties: upload_id: { type: string } bucket: { type: string } key: { type: string } chunk_size_bytes: { type: integer } part_urls: type: array items: type: object properties: part_number: { type: integer } url: { type: string } /projects/{project_id}/aep/complete-upload: post: tags: [Projects] summary: Complete multipart AEP upload (server hashes + registers) parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: type: object required: [upload_id, parts] properties: upload_id: { type: string } parts: type: array items: type: object properties: part_number: { type: integer } etag: { type: string } responses: '200': content: application/json: schema: type: object properties: aep_file_md5: { type: string } aep_file_size_bytes: { type: integer, format: int64 } # ===================== SCENES ===================== /projects/{project_id}/scenes: get: tags: [Scenes] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/Scene' } } post: tags: [Scenes] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/SceneCreate' } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/Scene' } /scenes/{scene_id}: get: tags: [Scenes] parameters: - { name: scene_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/SceneFull' } patch: tags: [Scenes] parameters: - { name: scene_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/SceneUpdate' } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/Scene' } delete: tags: [Scenes] parameters: - { name: scene_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '204': { description: Deleted } /scenes/{scene_id}/reorder: post: tags: [Scenes] summary: Bulk reorder scenes parameters: - { name: scene_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: type: object properties: ordered_ids: type: array items: { type: string, format: uuid } responses: '204': { description: Reordered } # ===================== ELEMENTS ===================== /scenes/{scene_id}/elements: get: tags: [Elements] parameters: - { name: scene_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: type: object properties: contents: { type: array, items: { $ref: '#/components/schemas/SceneContentElement' } } colors: { type: array, items: { $ref: '#/components/schemas/SceneColorElement' } } characters: { type: array, items: { $ref: '#/components/schemas/SceneCharacter' } } repeaters: { type: array, items: { $ref: '#/components/schemas/RepeaterItem' } } /scenes/{scene_id}/contents: post: tags: [Elements] parameters: - { name: scene_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/SceneContentElement' } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/SceneContentElement' } /scene-contents/{element_id}: patch: tags: [Elements] parameters: - { name: element_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/SceneContentElement' } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/SceneContentElement' } delete: tags: [Elements] parameters: - { name: element_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '204': { description: Deleted } /scenes/{scene_id}/colors: post: tags: [Elements] parameters: - { name: scene_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/SceneColorElement' } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/SceneColorElement' } /scene-colors/{element_id}: patch: tags: [Elements] parameters: - { name: element_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/SceneColorElement' } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/SceneColorElement' } delete: tags: [Elements] parameters: - { name: element_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '204': { description: Deleted } /projects/{project_id}/shared-layers: get: tags: [Elements] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/SharedLayer' } } post: tags: [Elements] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/SharedLayer' } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/SharedLayer' } /projects/{project_id}/shared-colors: get: tags: [Elements] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/SharedColor' } } # ===================== PRESETS ===================== /projects/{project_id}/preset-stories: get: tags: [Presets] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/PresetStory' } } post: tags: [Presets] parameters: - { name: project_id, in: path, required: true, schema: { type: string, format: uuid } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/PresetStoryCreate' } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/PresetStory' } /preset-stories/{preset_story_id}: get: tags: [Presets] parameters: - { name: preset_story_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/PresetStoryFull' } # ===================== SVG PREVIEW (color picker) ===================== /svg-previews/generate: post: tags: [SVGPreview] summary: | Generate SVG color preview by dropping an image. Calls AI Service. Returns generated SVG with named color zones. requestBody: required: true content: multipart/form-data: schema: type: object required: [image, target] properties: image: { type: string, format: binary } target_type: { type: string, enum: [project, scene] } target_id: { type: string, format: uuid } color_keys: type: array description: List of color element keys to detect items: type: object properties: element_key: { type: string } current_color: { type: string } responses: '202': description: Accepted; generation async, event content.svg_preview.generated.v1 fired content: application/json: schema: type: object properties: svg_preview_id: { type: string, format: uuid } status: { type: string, enum: [Generating] } /svg-previews/{svg_preview_id}: get: tags: [SVGPreview] parameters: - { name: svg_preview_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/SvgPreview' } /scenes/{scene_id}/svg-preview: get: tags: [SVGPreview] summary: Get current SVG preview for a scene parameters: - { name: scene_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/SvgPreview' } '404': { description: No preview generated yet } # ===================== FONTS ===================== /fonts: get: tags: [Fonts] responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/Font' } } post: tags: [Fonts] summary: (Admin) Upload font + register requestBody: required: true content: multipart/form-data: schema: type: object properties: file: { type: string, format: binary } name: { type: string } family: { type: string } direction: { type: string } is_premium: { type: boolean } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/Font' } /fonts/{font_id}: delete: tags: [Fonts] parameters: - { name: font_id, in: path, required: true, schema: { type: string, format: uuid } } responses: '204': { description: Deleted } # ===================== MUSIC ===================== /music: get: tags: [Music] parameters: - { name: q, in: query, schema: { type: string } } - { name: genre, in: query, schema: { type: string } } - { name: mood, in: query, schema: { type: string } } - { name: min_duration, in: query, schema: { type: number } } - { name: max_duration, in: query, schema: { type: number } } responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/MusicTrack' } } meta: { $ref: '#/components/schemas/PaginationMeta' } post: tags: [Music] summary: (Admin) Add music track requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/MusicTrack' } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/MusicTrack' } # ===================== CATEGORIES ===================== /categories: get: tags: [Categories] responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/Category' } } post: tags: [Categories] summary: (Admin) Create requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/Category' } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/Category' } /tags: get: tags: [Tags] responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/Tag' } } # ===================== FAVORITES ===================== /favorites/folders: get: tags: [Favorites] responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/FavoriteFolder' } } post: tags: [Favorites] requestBody: required: true content: application/json: schema: type: object required: [name] properties: name: { type: string } description: { type: string } responses: '201': content: application/json: schema: { $ref: '#/components/schemas/FavoriteFolder' } /favorites/containers: post: tags: [Favorites] summary: Add template to favorites requestBody: required: true content: application/json: schema: type: object required: [container_id] properties: container_id: { type: string, format: uuid } folder_id: { type: string, format: uuid } note: { type: string } responses: '201': { description: Added } delete: tags: [Favorites] parameters: - { name: container_id, in: query, required: true, schema: { type: string, format: uuid } } responses: '204': { description: Removed } # ===================== CMS ===================== /blogs: get: tags: [CMS] parameters: - { name: q, in: query, schema: { type: string } } - { name: page, in: query, schema: { type: integer } } responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/Blog' } } meta: { $ref: '#/components/schemas/PaginationMeta' } /blogs/{slug}: get: tags: [CMS] parameters: - { name: slug, in: path, required: true, schema: { type: string } } responses: '200': content: application/json: schema: { $ref: '#/components/schemas/Blog' } /comments: post: tags: [CMS] requestBody: required: true content: application/json: schema: type: object required: [content] properties: content: { type: string } rate: { type: number } blog_id: { type: string, format: uuid } container_id: { type: string, format: uuid } parent_comment_id: { type: string, format: uuid } responses: '201': { description: Created (pending approval) } /slides: get: tags: [CMS] responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/Slide' } } /home-events: get: tags: [CMS] responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/HomeEvent' } } /settings: get: tags: [CMS] summary: Tenant website settings (public subset) responses: '200': content: application/json: schema: type: object additionalProperties: true # ===================== PROJECT SERVERS ===================== /project-servers: get: tags: [ProjectServers] summary: List render servers (for node assignment) security: [ServiceToken: []] responses: '200': content: application/json: schema: type: object properties: data: { type: array, items: { $ref: '#/components/schemas/ProjectServer' } } components: securitySchemes: BearerAuth: { type: http, scheme: bearer, bearerFormat: JWT } ServiceToken: { type: http, scheme: bearer } schemas: PaginationMeta: type: object properties: page: { type: integer } page_size: { type: integer } total: { type: integer } has_more: { type: boolean } TemplateCard: type: object properties: container_id: { type: string, format: uuid } slug: { type: string } name: { type: string } image: { type: string } demo: { type: string } mini_demo: { type: string } is_premium: { type: boolean } is_mockup: { type: boolean } primary_mode: { type: string } rate_avg: { type: number } rate_count: { type: integer } use_count: { type: integer, format: int64 } available_aspects: type: array items: { type: string } TemplateDetail: type: object properties: container: { $ref: '#/components/schemas/Container' } projects: { type: array, items: { $ref: '#/components/schemas/ProjectSummary' } } categories: { type: array, items: { $ref: '#/components/schemas/Category' } } tags: { type: array, items: { $ref: '#/components/schemas/Tag' } } Container: type: object properties: id: { type: string, format: uuid } slug: { type: string } name: { type: string } description: { type: string } keywords: { type: string } news_text: { type: string } image: { type: string } demo: { type: string } full_demo: { type: string } mini_demo: { type: string } is_published: { type: boolean } is_premium: { type: boolean } is_mockup: { type: boolean } primary_mode: { type: string } rate_avg: { type: number } rate_count: { type: integer } use_count: { type: integer, format: int64 } ContainerCreate: type: object required: [slug, name, primary_mode] properties: slug: { type: string } name: { type: string } description: { type: string } primary_mode: { type: string } is_premium: { type: boolean } is_mockup: { type: boolean } category_ids: { type: array, items: { type: string, format: uuid } } tag_ids: { type: array, items: { type: string, format: uuid } } ContainerUpdate: allOf: - $ref: '#/components/schemas/ContainerCreate' ProjectSummary: type: object properties: id: { type: string, format: uuid } name: { type: string } aspect: { type: string } original_width: { type: integer } original_height: { type: integer } resolution: { type: string } project_duration_sec: { type: number } choose_mode: { type: string } image: { type: string } Project: allOf: - $ref: '#/components/schemas/ProjectSummary' - type: object properties: container_id: { type: string, format: uuid } project_server_id: { type: string, format: uuid } description: { type: string } full_demo: { type: string } download_link: { type: string } aep_file_url: { type: string } aep_file_md5: { type: string } aep_file_size_bytes: { type: integer, format: int64 } min_duration_sec: { type: number } max_duration_sec: { type: number } free_fps: { type: integer } vip_factor: { type: number } render_aep_comp: { type: string } is_published: { type: boolean } ProjectCreate: type: object required: [container_id, name, original_width, original_height, project_duration_sec, choose_mode] properties: container_id: { type: string, format: uuid } name: { type: string } original_width: { type: integer } original_height: { type: integer } aspect: { type: string } project_duration_sec: { type: number } min_duration_sec: { type: number } max_duration_sec: { type: number } choose_mode: { type: string } resolution: { type: string } free_fps: { type: integer } vip_factor: { type: number } render_aep_comp: { type: string } project_server_id: { type: string, format: uuid } ProjectUpdate: allOf: - $ref: '#/components/schemas/ProjectCreate' ProjectFull: type: object properties: project: { $ref: '#/components/schemas/Project' } scenes: { type: array, items: { $ref: '#/components/schemas/SceneFull' } } shared_colors: { type: array, items: { $ref: '#/components/schemas/SharedColor' } } shared_layers: { type: array, items: { $ref: '#/components/schemas/SharedLayer' } } character_controllers: { type: array, items: { $ref: '#/components/schemas/CharacterController' } } character_presets: { type: array, items: { $ref: '#/components/schemas/CharacterPreset' } } preset_stories: { type: array, items: { $ref: '#/components/schemas/PresetStory' } } Scene: type: object properties: id: { type: string, format: uuid } project_id: { type: string, format: uuid } key: { type: string } title: { type: string } localized_title: { type: object, additionalProperties: { type: string } } scene_type: { type: string } image: { type: string } demo: { type: string } snapshot_url: { type: string } default_duration_sec: { type: number } min_duration_sec: { type: number } max_duration_sec: { type: number } overlap_at_end_sec: { type: number } manual_color_selection: { type: boolean } sort: { type: integer } SceneCreate: type: object required: [key, title] properties: key: { type: string } title: { type: string } scene_type: { type: string } default_duration_sec: { type: number } sort: { type: integer } SceneUpdate: type: object properties: title: { type: string } default_duration_sec: { type: number } sort: { type: integer } manual_color_selection: { type: boolean } SceneFull: allOf: - $ref: '#/components/schemas/Scene' - type: object properties: contents: { type: array, items: { $ref: '#/components/schemas/SceneContentElement' } } colors: { type: array, items: { $ref: '#/components/schemas/SceneColorElement' } } color_presets: { type: array, items: { $ref: '#/components/schemas/SceneColorPreset' } } characters: { type: array, items: { $ref: '#/components/schemas/SceneCharacter' } } repeaters: { type: array, items: { $ref: '#/components/schemas/RepeaterItem' } } SceneContentElement: type: object properties: id: { type: string, format: uuid } scene_id: { type: string, format: uuid } repeater_item_id: { type: string, format: uuid, nullable: true } key: { type: string } title: { type: string } localized_title: { type: object, additionalProperties: { type: string } } hint: { type: string } type: { type: string } default_value: { type: string } font_id: { type: string, format: uuid, nullable: true } font_face: { type: string } font_size: { type: integer } justify: { type: string } position_in_container: { type: integer } is_text_box: { type: boolean } max_size: { type: integer, nullable: true } direction_layer_key: { type: string } direction_layer_value: { type: integer } video_support: { type: boolean } mapped_list: { type: array, items: { type: object } } ai_input_type: { type: string } is_hidden: { type: boolean } opacity_controller_key: { type: string } virtual_count: { type: integer } sort: { type: integer } SceneColorElement: type: object properties: id: { type: string, format: uuid } scene_id: { type: string, format: uuid } element_key: { type: string } title: { type: string } icon: { type: string } attr_value: { type: string, enum: [fill, stroke, tracking, dropshadow] } default_color: { type: string } sort: { type: integer } SceneColorPreset: type: object properties: id: { type: string, format: uuid } name: { type: string } sort: { type: integer } items: type: array items: type: object properties: element_key: { type: string } value: { type: string } sort: { type: integer } SceneCharacter: type: object properties: id: { type: string, format: uuid } key: { type: string } name: { type: string } icon: { type: string } controllers: type: array items: type: object properties: id: { type: string, format: uuid } name: { type: string } key: { type: string } default_value: { type: string } options: type: array items: type: object properties: name: { type: string } icon: { type: string } value: { type: string } SharedLayer: type: object properties: id: { type: string, format: uuid } project_id: { type: string, format: uuid } key: { type: string } title: { type: string } type: { type: string } default_value: { type: string } font_face: { type: string } font_size: { type: integer } justify: { type: string } position_in_container: { type: integer } is_text_box: { type: boolean } sort: { type: integer } SharedColor: type: object properties: id: { type: string, format: uuid } project_id: { type: string, format: uuid } element_key: { type: string } title: { type: string } icon: { type: string } attr_value: { type: string } default_color: { type: string } sort: { type: integer } RepeaterItem: type: object properties: id: { type: string, format: uuid } title: { type: string } repeat_box_key: { type: string } repeat_item_key: { type: string } max_repeat_count: { type: integer } sort: { type: integer } repeat_sort_strategy: { type: string } CharacterController: type: object properties: id: { type: string, format: uuid } name: { type: string } key: { type: string } sort: { type: integer } options: { type: array, items: { type: object } } CharacterPreset: type: object properties: id: { type: string, format: uuid } key: { type: string, format: uuid } name: { type: string } icon: { type: string } sort: { type: integer } controllers: type: array items: type: object properties: name: { type: string } key: { type: string } value: { type: string } PresetStory: type: object properties: id: { type: string, format: uuid } project_id: { type: string, format: uuid } name: { type: string } description: { type: string } demo: { type: string } music_id: { type: string, format: uuid } sort: { type: integer } is_published: { type: boolean } scene_count: { type: integer } PresetStoryCreate: type: object required: [name] properties: name: { type: string } description: { type: string } demo: { type: string } music_id: { type: string, format: uuid } scene_ids: type: array items: { type: string, format: uuid } PresetStoryFull: allOf: - $ref: '#/components/schemas/PresetStory' - type: object properties: scenes: type: array items: allOf: - $ref: '#/components/schemas/Scene' - type: object properties: sort_in_story: { type: integer } default_duration_sec: { type: number } SvgPreview: type: object properties: id: { type: string, format: uuid } project_id: { type: string, format: uuid, nullable: true } scene_id: { type: string, format: uuid, nullable: true } source_image_url: { type: string } svg_url: { type: string } thumbnail_url: { type: string } width: { type: integer } height: { type: integer } generated_by_ai: { type: boolean } quality_score: { type: number } color_zones: type: array items: type: object properties: element_key: { type: string } detected_color: { type: string } bbox: { type: array, items: { type: number } } created_at: { type: string, format: date-time } Font: type: object properties: id: { type: string, format: uuid } name: { type: string } original_name: { type: string } system_name: { type: string } family: { type: string } weight: { type: integer } style: { type: string } direction: { type: string } file_url: { type: string } sample_image_url: { type: string } is_premium: { type: boolean } installed_on_nodes: { type: boolean } MusicTrack: type: object properties: id: { type: string, format: uuid } name: { type: string } caption: { type: string } keywords: { type: string } url: { type: string } duration_sec: { type: number } bpm: { type: integer } genre: { type: string } mood: { type: string } is_premium: { type: boolean } waveform_data: { type: object } Category: type: object properties: id: { type: string, format: uuid } slug: { type: string } name: { type: string } description: { type: string } image_url: { type: string } icon: { type: string } parent_id: { type: string, format: uuid, nullable: true } sort: { type: integer } Tag: type: object properties: id: { type: string, format: uuid } slug: { type: string } name: { type: string } latin_name: { type: string } applies_to_mode: { type: string } FavoriteFolder: type: object properties: id: { type: string, format: uuid } name: { type: string } description: { type: string } item_count: { type: integer } created_at: { type: string, format: date-time } Blog: type: object properties: id: { type: string, format: uuid } slug: { type: string } kind: { type: string } title: { type: string } short_description: { type: string } content: { type: string } image: { type: string } cover: { type: string } author_display_name: { type: string } publish_date: { type: string, format: date-time } view_count: { type: integer, format: int64 } Slide: type: object properties: id: { type: string, format: uuid } title: { type: string } image: { type: string } keyword: { type: string } slide_type: { type: string } parameter: { type: string } sort: { type: integer } HomeEvent: type: object properties: id: { type: string, format: uuid } title: { type: string } subtitle: { type: string } description: { type: string } button_text: { type: string } button_url: { type: string } badge: { type: string } image: { type: string } color: { type: string } background_color: { type: string } text_color: { type: string } sort: { type: integer } ProjectServer: type: object properties: id: { type: string, format: uuid } name: { type: string } region: { type: string } ip: { type: string } default_project_address: { type: string } render_output_location: { type: string } minio_endpoint: { type: string } minio_bucket_templates: { type: string } minio_bucket_outputs: { type: string } is_active: { type: boolean }