--- title: Maester Enterprise emoji: 🎬 colorFrom: blue colorTo: green sdk: docker app_port: 7860 pinned: false license: mit --- # Maester Enterprise Maester Enterprise merges the five existing projects into one deployable workspace: - `services/ktts`: Kokoro ONNX text-to-speech - `services/musicgen`: MusicGen audio generation - `services/whisper`: Whisper operator, clipping, publishing, and automation - `services/ffmpeg_automation`: FFmpeg task hub - `services/render_engine`: Ava2lon/Basyx rendering engine The new enterprise gateway lives in `maester_enterprise/` and mounts each service under one secured FastAPI app. ## Service Routes | Service | Route | | --- | --- | | Gateway health | `/health` | | Gateway readiness | `/ready` | | Service catalog | `/services` | | KTTS | `/services/ktts` | | MusicGen | `/services/musicgen` | | Whisper | `/services/whisper` | | FFmpeg automation | `/services/ffmpeg` | | Render engine | `/services/render` | | Automation API | `/automation` | All non-public routes require `X-API-Key: ` in production. ## Enterprise Upgrades Added - Single gateway for all services - Shared API-key enforcement - Shared rate limiting - Request body size limits - Request IDs and security headers - Runtime directory isolation under `/app/data` - Service catalog and readiness endpoint - Per-service enable/disable environment flags - Docker and Docker Compose deployment files - `.env.example` for required secrets and integrations - Campaign, brand kit, approval, calendar, publishing, analytics, and A/B testing APIs - Hugging Face CPU-focused default Dockerfile with all services enabled ## Automation Features The `/automation` API adds the short-form production layer for TikTok, Instagram Reels, Facebook Shorts, and YouTube Shorts. | Feature | Endpoint | | --- | --- | | Capability map | `GET /automation/capabilities` | | Brand kits | `POST /automation/brand-kits`, `GET /automation/brand-kits` | | Create campaign | `POST /automation/campaigns` | | Create and generate variants | `POST /automation/campaigns/generate` | | Generate variants for existing campaign | `POST /automation/campaigns/{campaign_id}/generate` | | List campaign variants | `GET /automation/campaigns/{campaign_id}/variants` | | Approve/reject variant | `POST /automation/variants/{variant_id}/approval` | | Create render job plan | `POST /automation/variants/{variant_id}/render-job` | | Recommend schedule | `POST /automation/schedule/recommend` | | Apply schedule | `POST /automation/schedule/apply` | | Create publish job | `POST /automation/publish` | | Ingest analytics | `POST /automation/analytics/events` | | Analytics summary | `GET /automation/analytics/summary` | | A/B test plan | `POST /automation/ab-tests/{campaign_id}` | | Workspaces | `POST /automation/workspaces`, `GET /automation/workspaces` | | Team/client review | `GET /automation/client-portals/{workspace_id}/review-bundle` | | Social accounts | `POST /automation/accounts`, `GET /automation/accounts` | | Cross-post rules | `POST /automation/cross-post-rules`, `POST /automation/cross-post-rules/{rule_id}/plan` | | Trends | `POST /automation/trends`, `GET /automation/trends/recommendations` | | Competitors | `POST /automation/competitors`, `GET /automation/competitors/insights` | | Idea inbox | `POST /automation/ideas`, `POST /automation/ideas/{idea_id}/campaign` | | 30-90 day calendar plan | `POST /automation/calendar/plan` | | Quality scoring | `POST /automation/quality/hook`, `GET /automation/quality/variants/{variant_id}` | | Compliance check | `POST /automation/compliance/check` | | Integrations | `POST /automation/integrations`, `GET /automation/integrations/templates` | | Queue operations | `GET /automation/queue/summary`, `GET /automation/queue/next` | | Usage and reports | `POST /automation/usage`, `POST /automation/reports` | | CPU profile | `GET /automation/ops/cpu-profile` | ## Using the API from n8n HTTP Request Nodes The examples below assume n8n is running from this repository's Docker Compose stack. In that setup, use these expressions: ```text Base URL: ={{ ($env.MAESTER_BASE_URL || 'http://maester-enterprise:7860').replace(/\/$/, '') }} API key: ={{ $env.MAESTER_API_KEY }} ``` For an n8n instance running outside Docker, set `MAESTER_BASE_URL` to the URL that n8n can use to reach Maester, for example `http://host.docker.internal:7860` or the public HTTPS URL of the deployment. `localhost` means the n8n container itself when n8n runs in Docker. ### Common HTTP Request node settings Use these settings for every protected endpoint: | n8n field | Value | | --- | --- | | Authentication | `None`, or an n8n Header Auth credential | | Send Headers | On | | Header name | `X-API-Key` | | Header value | `={{ $env.MAESTER_API_KEY }}` | | URL | `={{ $env.MAESTER_BASE_URL + '/path' }}` | | Response Format | `JSON` unless the endpoint returns a file | | Timeout | At least `120000` ms for AI operations and `600000` ms for synchronous media operations | An n8n Header Auth credential with the header name `X-API-Key` is preferable when workflow editors must not be able to read environment variables. The gateway also accepts `api_key` as a query parameter, but headers avoid leaking the key into URLs and logs. For a JSON request, enable **Send Body**, select **JSON**, and enter an object or an expression such as: ```javascript ={{ { name: $json.name, topic: $json.topic, platforms: ['tiktok', 'instagram_reels'], quantity: 5 } }} ``` For query parameters, enable **Send Query Parameters** and add each named parameter shown in the tables below. Values in `{braces}` are path parameters; replace them with n8n expressions, for example: ```text ={{ $env.MAESTER_BASE_URL + '/automation/campaigns/' + $json.id }} ``` For file responses, set **Response Format** to **File** and choose an output binary property such as `data`. For multipart uploads, select **Form-Data** and use an **n8n Binary File** parameter for each file field. Do not manually set `Content-Type` for multipart requests; n8n must generate the boundary. ### Gateway endpoints | Method and path | n8n use | | --- | --- | | `GET /health` | Public health check. No body; JSON response. | | `GET /ready` | Public readiness check. No body; JSON response. A `207` response means the gateway started but one or more embedded services failed to mount. | | `GET /version` | Public version check. No body; JSON response. | | `GET /api/meta` | Add `X-API-Key`; returns version, security mode, mounted services, and mount failures. | | `GET /services` | Add `X-API-Key`; returns the enabled and mounted service catalog. | | `GET /` | Browser redirect to `/studio/`; it is not normally useful in an n8n workflow. | ### Automation endpoint payloads Automation POST and PATCH routes use a JSON body unless a table explicitly says `query` or `multipart`. These minimal bodies can be pasted into an n8n HTTP Request node and populated with expressions from earlier nodes. ```json { "brand_kit": { "name": "Acme", "primary_color": "#111111", "accent_color": "#16a34a" }, "campaign": { "name": "Launch week", "topic": "Three ways to automate customer support", "platforms": ["tiktok", "instagram_reels"], "quantity": 5 }, "approval": { "state": "approved", "reviewer": "Editorial team", "notes": "Ready to render" }, "publish": { "variant_id": "var_id", "platforms": ["tiktok"], "draft": true }, "analytics_event": { "variant_id": "var_id", "platform": "tiktok", "views": 1000, "likes": 100, "completion_rate": 0.72 }, "workspace": { "name": "Acme workspace", "client_name": "Acme" }, "member": { "workspace_id": "ws_id", "email": "editor@example.com", "role": "editor" }, "social_account": { "workspace_id": "ws_id", "platform": "tiktok", "handle": "@acme", "metadata": { "token_env": "TIKTOK_ACCESS_TOKEN", "publish_endpoint": "https://publisher.example.com/posts" } }, "cross_post_rule": { "name": "TikTok to Reels", "source_platform": "tiktok", "target_platforms": ["instagram_reels", "facebook_shorts"] }, "trend": { "platform": "tiktok", "keyword": "automation", "niche": "small business", "score": 75, "velocity": 12 }, "competitor": { "platform": "tiktok", "handle": "@competitor", "niche": "small business" }, "idea": { "text": "Turn one customer question into five short videos", "niche": "small business", "priority": 4 }, "calendar_plan": { "niche": "small business", "days": 30, "posts_per_day": 2, "platforms": ["tiktok", "instagram_reels"] }, "compliance": { "platform": "tiktok", "title": "Automation tips", "caption": "Three practical ideas", "hashtags": ["automation"], "duration_seconds": 30 }, "integration": { "kind": "n8n", "name": "Production n8n", "endpoint_url": "https://n8n.example.com/webhook/maester" }, "experiment": { "campaign_id": "camp_id", "name": "Hook test", "variable": "hook", "variant_ids": ["var_a", "var_b"] } } ``` The outer labels above are examples only. Send the value of one label as the actual request body, not the complete combined object. For example, use `={{ $json.campaign }}` when the incoming item contains the object above. ### Automation campaign and publishing endpoints | Method and path | n8n body or parameters | Result/use | | --- | --- | --- | | `GET /automation/capabilities` | None | Discover supported platforms and automation features. | | `POST /automation/brand-kits` | JSON `brand_kit` | Create a brand kit. | | `GET /automation/brand-kits` | None | List brand kits. | | `GET /automation/brand-kits/{brand_id}` | Path `brand_id` | Fetch one brand kit. | | `POST /automation/campaigns` | JSON `campaign` | Create a campaign without generating variants. | | `GET /automation/campaigns` | None | List campaigns. | | `GET /automation/campaigns/{campaign_id}` | Path `campaign_id` | Fetch one campaign. | | `POST /automation/campaigns/{campaign_id}/generate` | Path only; no body | Generate deterministic variants for an existing campaign. | | `POST /automation/campaigns/generate` | JSON `campaign` | Create a campaign and generate variants in one request. | | `POST /automation/campaigns/{campaign_id}/generate-ai` | JSON `{"provider":"openai","model":"gpt-4o-mini","instructions":"Use a direct tone"}` | Generate variants with the configured OpenAI, Gemini, or OpenRouter provider. | | `GET /automation/providers` | None | Check selected AI provider and configuration status. | | `GET /automation/campaigns/{campaign_id}/variants` | Path `campaign_id` | List variants for one campaign. | | `GET /automation/variants` | None | List every variant. | | `GET /automation/variants/{variant_id}` | Path `variant_id` | Fetch one variant. | | `PATCH /automation/variants/{variant_id}` | JSON containing any of `title`, `hook`, `script`, `caption`, `hashtags`, `cta`, `template`, `creative_style`, `duration_seconds`, `safe_zone`, `render_payload`, or `metadata` | Edit a variant and increment its revision. | | `DELETE /automation/variants/{variant_id}` | Path only | Delete a variant. | | `POST /automation/variants/{variant_id}/approval` | JSON `approval` | Approve, reject, or move a variant to another approval state. | | `POST /automation/variants/{variant_id}/rewrite` | Path only; no body | Rewrite the variant script. | | `POST /automation/variants/{variant_id}/render-job` | Path only; no body | Create an automation job containing the render-engine payload. | | `POST /automation/repurpose` | JSON `{"name":"Podcast clips","transcript":"...","quantity":5,"platforms":["tiktok"]}` | Split long-form text into a campaign and short-form variants. | | `POST /automation/schedule/recommend` | Query `campaign_id`; optional `start_at` ISO timestamp | Return recommended slots without saving them. | | `POST /automation/schedule/apply` | Query `campaign_id`; optional `start_at` | Save recommended calendar entries. | | `GET /automation/calendar` | None | List saved calendar entries. | | `POST /automation/publish` | JSON `publish` | Queue or schedule an approved variant for publishing. | | `GET /automation/jobs` | None | List all automation jobs. | | `GET /automation/jobs/{job_id}` | Path `job_id` | Fetch one job. | | `POST /automation/jobs/{job_id}/state` | JSON `{"state":"ready","result":{},"logs":["Render complete"]}` | Update a job from an external worker. | | `POST /automation/jobs/{job_id}/retry` | Path only; no body | Requeue a failed or cancelled job. | | `POST /automation/jobs/{job_id}/cancel` | Path only; no body | Cancel a non-terminal job. | | `POST /automation/analytics/events` | JSON `analytics_event` | Store metrics and compute a viral score. | | `GET /automation/analytics/summary` | None | Return platform averages, winners, and recommendations. | | `POST /automation/ab-tests/{campaign_id}` | Path only; no body | Build a simple A/B grouping plan. | ### Automation workspace, research, and operations endpoints | Method and path | n8n body or parameters | Result/use | | --- | --- | --- | | `POST /automation/workspaces` | JSON `workspace` | Create a workspace. | | `GET /automation/workspaces` | None | List workspaces. | | `POST /automation/workspaces/{workspace_id}/members` | JSON `member`; the path value overrides `workspace_id` in the body | Add a member. | | `GET /automation/workspaces/{workspace_id}/members` | Path only | List workspace members. | | `GET /automation/workspaces/{workspace_id}/quota` | Path only | Return usage and remaining quota. | | `POST /automation/accounts` | JSON `social_account` | Register a social account and connector metadata. | | `GET /automation/accounts` | Optional query `workspace_id` | List social accounts. | | `GET /automation/accounts/{account_id}/connector` | Path only | Check whether publishing and analytics credentials are configured. | | `GET /automation/accounts/{account_id}/oauth-url` | Path plus required query `redirect_uri` | Generate an OAuth authorization URL. Follow the URL in a browser; an n8n HTTP node should not automatically follow this interactive flow. | | `POST /automation/accounts/{account_id}/analytics-sync` | Path only; no body | Queue an analytics synchronization job. | | `POST /automation/cross-post-rules` | JSON `cross_post_rule` | Create a cross-post rule. | | `GET /automation/cross-post-rules` | Optional query `workspace_id` | List cross-post rules. | | `POST /automation/cross-post-rules/{rule_id}/plan` | Path `rule_id`; required query `variant_id` | Plan delayed target-platform posts. | | `POST /automation/trends` | JSON `trend` | Store a trend signal. | | `GET /automation/trends` | Optional query `niche` | List trend signals. | | `GET /automation/trends/recommendations` | Optional query `niche` | Rank trend recommendations. | | `POST /automation/competitors` | JSON `competitor` | Store a competitor profile. | | `GET /automation/competitors` | Optional query `niche` | List competitor profiles. | | `GET /automation/competitors/insights` | Optional query `niche` | Summarize observed hooks and formats. | | `POST /automation/ideas` | JSON `idea` | Add an idea to the inbox. | | `GET /automation/ideas` | Optional queries `workspace_id` and `status` | List and filter ideas. | | `POST /automation/ideas/{idea_id}/campaign` | Path only; no body | Convert an idea into a campaign. | | `POST /automation/calendar/plan` | JSON `calendar_plan` | Generate a 1-90 day content plan. | | `POST /automation/quality/hook` | JSON `{"hook":"Stop doing this manually"}` | Score a hook. | | `POST /automation/quality/script` | JSON `{"script":"..."}` | Score a script. | | `POST /automation/quality/caption` | JSON `{"caption":"..."}` | Score a caption. | | `GET /automation/quality/variants/{variant_id}` | Path only | Score all text fields and predict retention for a variant. | | `POST /automation/compliance/check` | JSON `compliance` | Check platform, duration, disclosure, caption, and hashtag rules. | | `POST /automation/integrations` | JSON `integration` | Register an outbound webhook or service integration. | | `GET /automation/integrations` | Optional query `workspace_id` | List integrations. | | `GET /automation/integrations/templates` | None | Return n8n, Zapier, Make, Slack, Drive, and Shopify integration recipes. | | `POST /automation/usage` | JSON `{"workspace_id":"ws_id","kind":"render_minutes","quantity":1,"cost_units":2.5}` | Log usage. | | `GET /automation/usage/summary` | Optional query `workspace_id` | Summarize usage. | | `GET /automation/queue/summary` | None | Count jobs by state. | | `GET /automation/queue/next` | Optional query `kind` | Inspect the next queued job. | | `POST /automation/queue/process` | Optional queries `kind` and `limit` (1-25) | Process queued automation jobs. | | `POST /automation/reports` | JSON `{"workspace_id":"ws_id","format":"json","include_recommendations":true}` | Build a campaign/workspace report. | | `GET /automation/client-portals/{workspace_id}/review-bundle` | Path only | Return variants awaiting client review. | | `GET /automation/overview` | Optional query `workspace_id` | Return dashboard totals and summaries. | | `GET /automation/ops/cpu-profile` | None | Return recommended CPU deployment settings. | | `POST /automation/ops/warmup` | No body | Return the CPU-safe warmup plan. | ### Automation assets, experiments, reviews, and rules | Method and path | n8n body or parameters | Result/use | | --- | --- | --- | | `POST /automation/assets` | Multipart Form-Data: binary field `file`; optional text fields `workspace_id`, `license`, `attribution`, and comma-separated `tags` | Upload and register an asset. | | `GET /automation/assets` | Optional queries `workspace_id`, `kind`, and `tag` | List assets. | | `GET /automation/assets/{asset_id}/usage` | Path only | Find variants that reference an asset. | | `DELETE /automation/assets/{asset_id}` | Optional query `force=true` | Delete an unused asset, or force deletion. | | `POST /automation/experiments` | JSON `experiment` | Create a multi-variant experiment. | | `GET /automation/experiments` | Optional query `campaign_id` | List experiments. | | `POST /automation/experiments/{experiment_id}/evaluate` | Path only; no body | Recalculate confidence and choose a winner when thresholds are met. | | `POST /automation/experiments/{experiment_id}/generate-followups` | Optional query `quantity` (1-10) | Generate variants based on the winner. | | `POST /automation/reviews` | JSON `{"workspace_id":"ws_id","variant_ids":["var_a","var_b"],"expires_in_days":7,"created_by":"Producer"}` | Create a signed public review link. Save the returned `token` or `review_url`. | | `GET /automation/reviews/public/{token}` | Path token; API key not required while the token is valid | Fetch the public review bundle. | | `POST /automation/reviews/public/{token}/comments` | JSON `{"variant_id":"var_a","author":"Client","body":"Shorten the intro"}` | Add a review comment; API key is not required with a valid token. | | `POST /automation/reviews/public/{token}/decision` | Required query `variant_id`; JSON `approval` | Approve/reject through a public review link. | | `POST /automation/rules` | JSON `{"name":"Render approvals","event":"variant_approved","actions":[{"type":"render"}]}` | Create an event rule. | | `GET /automation/rules` | Optional query `workspace_id` | List rules. | | `POST /automation/rules/trigger` | Required queries `event` and `resource_id`; optional `workspace_id`. Send `metadata` as a JSON query value only when needed. | Trigger matching notify, render, or publish actions. | | `GET /automation/notifications` | Optional query `workspace_id` | List notifications. | | `POST /automation/notifications/dispatch` | Query `integration_id`, `subject`, `message`; optional `workspace_id` | Queue delivery through an integration. | | `GET /automation/audit` | Optional queries `workspace_id` and `limit` (1-500) | List audit events. | ### Kokoro TTS endpoint Use `POST /services/ktts/v1/audio/speech`. Create an HTTP Request node with URL `={{ $env.MAESTER_BASE_URL + '/services/ktts/v1/audio/speech' }}`, JSON body, and **Response Format: File**: ```json { "input": "This narration was generated by Maester.", "voice": "af_bella.pt", "model": "kokoro-v0_19.onnx", "speed": 1.0 } ``` The WAV file is written to the node's selected binary property. Pass that property to a render upload node or another media-processing node. ### MusicGen endpoints | Method and path | n8n use | | --- | --- | | `GET /services/musicgen/health` | No body; JSON response. | | `POST /services/musicgen/generate` | Add query parameters `prompt` and `duration` (5-60 seconds). Set **Response Format: File** to receive `music.wav`. Example URL: `={{ $env.MAESTER_BASE_URL + '/services/musicgen/generate' }}`. | Music generation can be slow on CPU. Set a long timeout and use the render engine's asynchronous `POST /music/generate` route when polling is preferable. ### Whisper endpoints Use `POST /services/whisper/execute/{task_name}` with **Form-Data**. Supply one of these inputs: - `file`: an n8n binary file; - `url_input`: a downloadable media URL; or - `source`: another supported source string. Optional fields are `webhook` and a JSON request body only for tasks whose operation consumes publishing metadata. Because the route accepts multipart data, normal file-based calls should use Form-Data. Supported `{task_name}` values are `autonomous`, `auto-publish`, `publish`, `bulk-publish`, `generate-metadata`, `generate-thumbnail`, `schedule-post`, `transcribe`, `subtitles`, `render`, `highlights`, `viral-score`, `strategy`, `batch`, and `clips`. | Method and path | n8n use | | --- | --- | | `POST /services/whisper/execute/{task_name}` | Multipart request described above. Use JSON response for analysis tasks. For `render` and `generate-thumbnail`, set **Response Format: File** when the response is media. | | `GET /services/whisper/api/health` | No body; returns operator and auth status. | | `GET /services/whisper/api/status/{job_id}` | Poll a `batch` job using the returned `job_id`. | | `POST /services/whisper/api/auth/signup` | Optional auth subsystem; JSON `{"email":"user@example.com","username":"user_name","password":"at-least-8-characters"}`. | | `POST /services/whisper/api/auth/login` | Optional auth subsystem; JSON `{"email":"user@example.com","password":"..."}`. | | `POST /services/whisper/api/auth/verify` | Optional auth subsystem; JSON `{"token":"..."}`. | | `POST /services/whisper/api/auth/refresh` | Optional auth subsystem; JSON `{"token":"..."}`. | | `POST /services/whisper/api/auth/logout` | Optional auth subsystem; no body. | The auth routes exist only when the Whisper auth dependencies initialize successfully. Gateway `X-API-Key` authentication is still required in production even when using a Whisper login token. ### FFmpeg automation endpoints The `/n8n/*` routes are the preferred FFmpeg routes for n8n because they accept multipart binary fields with any name, URL inputs, base64 JSON, or a raw binary body. | Method and path | n8n use | | --- | --- | | `GET /services/ffmpeg/healthz` | Dependency health details. | | `GET /services/ffmpeg/readyz` | Readiness check; returns `503` when FFmpeg or required storage is unavailable. | | `GET /services/ffmpeg/tasks` | Discover task IDs, accepted file types, file counts, and output extensions. | | `POST /services/ffmpeg/n8n/execute/{task_id}` | Run synchronously. Use Form-Data, URL JSON, base64 JSON, or raw binary. Set **Response Format: File**. | | `POST /services/ffmpeg/n8n/jobs/{task_id}` | Submit asynchronously with the same input formats. JSON response contains `job_id`. | | `POST /services/ffmpeg/execute/{task_id}` | Standard multipart synchronous endpoint. Binary field name must be `files`. Set **Response Format: File**. | | `POST /services/ffmpeg/jobs/{task_id}` | Standard multipart asynchronous endpoint. Binary field name must be `files`. | | `GET /services/ffmpeg/status/{job_id}` | Poll until `status` is complete; response then includes `download_url`. | | `GET /services/ffmpeg/download/{job_id}` | Set **Response Format: File** to download a completed job. | | `GET /services/ffmpeg/history` | List recent outputs. | | `GET /services/ffmpeg/history/{history_id}/download` | Set **Response Format: File** to download a retained history item. | Available `{task_id}` values: ```text normalize, extract_audio, resize_916, add_subtitles, burn_lyrics, text_overlay, merge_music, thumbnail, watermark, compress, batch_compress, make_gif, tiktok_lyrics, tiktok_pro_reframer, reels_blur_fit, reels_safe_caption, reels_hook_title, reels_progress_bar, reels_loop, reels_subtitle_safe, reels_reaction_stack, reels_audio_duck, faceless_quote_card, faceless_story_pages, faceless_image_narration, faceless_video_narration, faceless_broll_montage, series_split_pack, series_episode_badge, series_batch_pack, series_recap_card, concat, slideshow, trim, crop_aspect, waveform, extract_frames, add_intro_outro, speed, remove_audio, replace_audio ``` For URL input, send JSON and keep the response as a file: ```json { "url": "https://cdn.example.com/input.mp4", "text": "Episode 1", "duration": "30", "crf": "23", "preset": "veryfast" } ``` For base64 input, send: ```json { "files": [ { "fileName": "input.mp4", "mimeType": "video/mp4", "data": "base64-data-from-a-previous-node" } ], "options": { "text": "Episode 1", "aspect_ratio": "9:16" } } ``` Recognized option fields are `text`, `start_time`, `end_time`, `duration`, `aspect_ratio`, `resolution`, `crf`, `preset`, `audio_bitrate`, `volume`, `position`, `opacity`, `fps`, `width`, `speed`, `timestamp`, `image_duration`, `frame_rate`, `font_size`, and `wave_color`. Call `GET /services/ffmpeg/tasks` before building a dynamic workflow to validate the required number and type of files for the selected task. ### Render engine request bodies Most render-engine mutation routes return a `job_id`, `status_url`, and signed `download_url`. The following are the main JSON body shapes. Render request: ```json { "template": "tiktok_classic", "preset": "tiktok_9_16_fast", "output_name": "campaign_clip.mp4", "voiceover": "https://cdn.example.com/voice.wav", "background_music": "https://cdn.example.com/music.mp3", "auto_subtitles": true, "audio_normalize": true, "scenes": [ { "start": 0, "duration": 5, "media": "https://cdn.example.com/scene.mp4", "caption": "Launch faster", "transition": "fade" } ] } ``` Toolkit request: ```json { "task": "trim", "media": "https://cdn.example.com/input.mp4", "output_name": "trimmed.mp4", "params": { "start": 2, "duration": 15 } } ``` Project and timeline requests: ```json { "create_project": { "name": "Launch edit", "metadata": {}, "export_settings": {"preset": "tiktok"} }, "add_timeline_item": { "project_id": "project_id", "track_type": "video", "item": {"source": "https://cdn.example.com/clip.mp4", "start": 0} }, "timeline_operation": { "project_id": "project_id", "operation": "trim", "item_id": "item_id", "params": {"start": 1, "end": 8} } } ``` As with the automation payload collection, send only the selected nested value. ### Render engine discovery and project endpoints | Method and path | n8n body or parameters | Result/use | | --- | --- | --- | | `GET /services/render/health` | None | Service health. | | `GET /services/render/monitor` | None | Runtime and queue monitoring. | | `GET /services/render/queue` | None | Queue summary. | | `GET /services/render/workers` | None | Worker and transcription configuration. | | `GET /services/render/presets` | None | Render presets, caption templates, styles, effects, and transitions. | | `GET /services/render/platforms` | None | Platform profiles. | | `GET /services/render/toolkit/tasks` | None | Supported toolkit task names. | | `GET /services/render/capabilities` | None | Complete studio capability catalog. | | `GET /services/render/effects` | None | Effects catalog. | | `GET /services/render/filters` | None | Filter formats and presets. | | `GET /services/render/transitions` | None | Transition families and FFmpeg transitions. | | `GET /services/render/templates/catalog` | None | Template, preset, and style catalog. | | `GET /services/render/projects` | None | List projects. | | `POST /services/render/projects` | JSON `create_project` | Create a project. | | `POST /services/render/project/create` | Same as `/services/render/projects` | Compatibility alias for project creation. | | `GET /services/render/project/{project_id}` | Path only | Fetch a project. | | `POST /services/render/project/save` | JSON `{"project_id":"id","project":{...}}` | Save a complete project document. | | `POST /services/render/project/assets/add` | JSON `{"project_id":"id","asset":{"source":"https://..."}}` | Add an asset record to a project. | | `POST /services/render/timeline/add` | JSON `add_timeline_item` | Add an item to a timeline track. | | `POST /services/render/timeline/operation` | JSON `timeline_operation` | Run `drag`, `split`, `trim`, `ripple_delete`, `insert`, `replace`, `group`, `lock`, `hide`, or `duplicate`. | | `POST /services/render/timeline/split` | Timeline-operation JSON; `operation` may be omitted | Split an item. | | `POST /services/render/timeline/trim` | Timeline-operation JSON; `operation` may be omitted | Trim an item. | | `POST /services/render/timeline/ripple-delete` | Timeline-operation JSON; `operation` may be omitted | Ripple-delete an item. | | `POST /services/render/timeline/insert` | Timeline-operation JSON; `operation` may be omitted | Insert an item. | | `POST /services/render/timeline/replace` | Timeline-operation JSON; `operation` may be omitted | Replace an item. | | `POST /services/render/timeline/group` | Timeline-operation JSON; `operation` may be omitted | Group items. | | `POST /services/render/timeline/lock` | Timeline-operation JSON; `operation` may be omitted | Lock an item. | | `POST /services/render/timeline/hide` | Timeline-operation JSON; `operation` may be omitted | Hide an item. | | `POST /services/render/timeline/duplicate` | Timeline-operation JSON; `operation` may be omitted | Duplicate an item. | | `POST /services/render/project/{project_id}/render` | JSON `{"output_name":"project.mp4","preset":"tiktok_9_16_fast"}` | Render a saved project asynchronously. | ### Render engine editing, generation, and media endpoints | Method and path | n8n body or parameters | Result/use | | --- | --- | --- | | `POST /services/render/effect/apply` | JSON `{"project_id":"id","target_id":"item","effect":"glitch","params":{}}` | Save an effect to a project item, or submit an async effect job when project IDs are omitted. | | `POST /services/render/filter/apply` | JSON `{"project_id":"id","target_id":"item","filter":"cinema","params":{}}` | Save or asynchronously apply a filter. | | `POST /services/render/transition/add` | JSON `{"project_id":"id","from_item_id":"a","to_item_id":"b","transition":"fade","duration":0.45}` | Add or asynchronously generate a transition. | | `POST /services/render/keyframe/add` | JSON `{"project_id":"id","target_id":"item","property":"opacity","time":1.5,"value":0.5}` | Add a keyframe. | | `POST /services/render/caption/generate` | JSON with one of `media`, `audio`, `text`, `transcript`, or `events`; optional `template`, `language`, and caption flags | Submit caption generation. | | `POST /services/render/music/generate` | JSON `{"prompt":"calm cinematic background","provider":"musicgen","params":{"duration":20}}` | Submit music generation. | | `POST /services/render/voice/generate` | JSON `{"text":"Narration text","provider":"kokoro","params":{"voice":"af_bella.pt"}}` | Submit voice generation. | | `POST /services/render/image/generate` | JSON `{"prompt":"vertical product background","provider":"flux","params":{}}` | Submit image generation. | | `POST /services/render/video/generate` | JSON `{"prompt":"slow camera push through an office","provider":"wan","params":{}}` | Submit video generation. | | `POST /services/render/ai/{tool}` | JSON `{"media":"...","transcript":"...","platform":"tiktok","params":{}}` | Submit an AI editing tool such as `auto_edit`, `auto_reframe`, or `auto_highlight_detection`. Discover names through `/services/render/capabilities`. | | `POST /services/render/assistant/{tool}` | Same general AI body | Run `script_writer`, `hook_generator`, `title_generator`, `description_generator`, `hashtag_generator`, `seo_optimizer`, `thumbnail_prompt_generator`, `b_roll_planner`, or `storyboard_generator`. | | `POST /services/render/render` | JSON render request | Submit a normal render or AI Reels-shaped request. | | `POST /services/render/render/ai-reels` | JSON `{"script":"...","voiceover":"https://...","assets":["https://..."],"template":"tiktok_classic"}` | Submit AI Reels assembly. | | `POST /services/render/render/batch` | JSON `{"jobs":[, ]}` | Submit multiple renders. | | `POST /services/render/automation/batch` | Same batch JSON | Authenticated alias for batch rendering. | | `POST /services/render/ingest` | JSON `{"sources":[{"url":"https://...","type":"video"}]}` | Stage remote sources asynchronously. | | `POST /services/render/analyze` | JSON `{"media":"https://...","transcript":"...","platform":"tiktok"}` | Analyze media. | | `POST /services/render/clips` | JSON `{"media":"https://...","clips":[{"start":0,"end":10}]}` | Create clips. | | `POST /services/render/thumbnail` | JSON `{"media":"https://...","text":"Watch this","timestamp":2,"template":"bold"}` | Generate a thumbnail. | | `POST /services/render/thumbnail/create` | Same thumbnail JSON | Alias for thumbnail generation. | | `POST /services/render/metadata` | JSON `{"topic":"automation","transcript":"...","platform":"tiktok"}` | Generate platform metadata. | | `POST /services/render/publish` | JSON `{"media":"path-or-url","title":"Title","platforms":["tiktok"],"draft":true}` | Submit a platform publishing task. | | `POST /services/render/toolkit` | JSON toolkit request | Run an FFmpeg/platform toolkit task asynchronously. | | `POST /services/render/edit` | Same toolkit JSON | Alias for `/services/render/toolkit`. | | `POST /services/render/transcribe` | JSON `{"audio":"https://...","model_size":"tiny","language":"en","word_timestamps":true}` | Transcribe synchronously. | | `POST /services/render/subtitles` | JSON `{"events":[{"start":0,"end":2,"text":"Hello"}],"format":"srt","template":"tiktok_classic"}`; **Response Format: File** | Generate an SRT or ASS file. | | `POST /services/render/scene-builder` | JSON `{"script":"...","assets":["https://..."],"duration":20,"transition":"fade"}` | Build scene JSON for a subsequent `/services/render/render` request. | | `POST /services/render/inspect` | Query `path` | Inspect a server-side or uploaded asset with FFprobe. | ### Render uploads, polling, and downloads | Method and path | n8n body or parameters | Result/use | | --- | --- | --- | | `POST /services/render/assets/upload` | Form-Data with one or more binary fields named `files` | Stage files and return `path` plus `upload://filename` references. | | `POST /services/render/upload` | Form-Data `files`; optional text field `expand_zip` | Stage files and optionally expand ZIP archives. | | `POST /services/render/render/upload` | Form-Data text field `request_json` containing a stringified render request, plus binary `files`. Refer to files as `upload://filename` inside `request_json`. | Submit a render using uploaded files. | | `POST /services/render/render/ai-reels/upload` | Same multipart pattern using an AI Reels request in `request_json`. | Submit AI Reels assembly using uploaded files. | | `POST /services/render/transcribe/upload` | Form-Data binary field `file`; optional text fields `model_size`, `language`, `task`, `beam_size`, `vad_filter`, `word_timestamps` | Transcribe an n8n binary file. | | `GET /services/render/status?job_id={job_id}` | Required query `job_id`. | Query form of job polling. | | `GET /services/render/status/{job_id}` | Path `job_id`. | Path form of job polling. Wait until `state` is `COMPLETED`. | | `POST /services/render/cancel/{job_id}` | Path only; no body | Cancel a queued/running job. | | `POST /services/render/admin/cleanup` | Optional query `older_than_seconds` | Remove old job files and records. | | `GET /services/render/download?job_id={job_id}&token={token}` | Required query `job_id`; include the returned `token`; **Response Format: File**. | Query-form download. | | `GET /services/render/download/{job_id}?token={token}` | Path `job_id`; include the returned `token`; **Response Format: File**. | Path-form download. Preserve the signed token returned by the submission endpoint. | For async jobs, connect the submission node to a **Wait** node, then a status HTTP Request node. Loop while the state is `QUEUED` or `RUNNING`; download only after `COMPLETED`. A typical status URL expression is: ```text ={{ $env.MAESTER_BASE_URL + '/services/render/status/' + $json.job_id }} ``` The submission response's `status_url` and `download_url` are relative paths. Prefix them with `MAESTER_BASE_URL` in n8n. Keep the `token` query string intact when constructing a download URL. ## n8n Workflow Examples Importable workflows live in `workflows/n8n/`. - `ai_provider_smoke_test.json`: tests the configured n8n AI provider with OpenAI, Gemini, or OpenRouter and returns the raw provider response. - `webhook_tiktok_storytelling_pexels.json`: exposes a POST webhook in n8n, accepts a topic/campaign payload, finds Pexels portrait clips, submits the render to Maester, and returns the render job response. - `tiktok_storytelling_pexels.json`: builds a TikTok storytelling mini-series episode, searches Pexels videos per scene, adds captions/text overlay metadata, applies background/effect settings, and submits the result to `/services/render/render`. - `autonomous_tiktok_storytelling_miniseries.json`: full workflow for `The Girl Who Disappeared Every Midnight`, including LLM strict JSON story generation, Maester campaign logging, quality/compliance checks, Pexels scene search, KTTS narration, render upload, and final TikTok render submission. - `islamic_motivation_tiktok_pipeline.json`: daily Islamic motivation TikTok automation with GPT-4o topic/scenes, Pexels clips, Maester FFmpeg resize/concat/caption/audio tasks, Archive.org nasheed fallback, validation, TikTok dry-run/publish, Google Sheets logging, and Telegram notifications. Example campaign request: ```json { "name": "Fitness lead magnet week 1", "topic": "simple home workouts for busy founders", "niche": "fitness coaching", "source_url": "https://example.com/source.mp4", "platforms": ["tiktok", "instagram_reels", "facebook_shorts"], "quantity": 12, "tone": "direct, useful, high-retention", "target_audience": "busy professionals" } ``` ## Run With Docker Compose ```bash cp .env.example .env # Edit .env and set MAESTER_API_KEY, N8N_ENCRYPTION_KEY, and any provider secrets you need. docker compose up --build ``` Then call: ```bash curl http://localhost:7860/health curl -H "X-API-Key: $MAESTER_API_KEY" http://localhost:7860/services ``` n8n runs with the same stack at: ```text http://localhost:5678 ``` Inside n8n imports, `MAESTER_BASE_URL` is already set to `http://maester-enterprise:7860`, so workflow HTTP nodes can call Maester over the Docker network. Import the JSON files from `workflows/n8n/`, then activate the webhook or scheduled workflows you want to use. The n8n container also receives provider-neutral AI configuration: ```env AI_PROVIDER=openai|gemini|openrouter LLM_PROVIDER=openai|gemini|openrouter LLM_MODEL=gpt-4o-mini OPENAI_API_KEY= GEMINI_API_KEY= OPENROUTER_API_KEY= ``` Use `workflows/n8n/ai_provider_smoke_test.json` to verify the selected provider before wiring it into a production workflow. ## Hugging Face CPU Mode The default [Dockerfile](Dockerfile) is CPU-focused for Hugging Face Spaces and enables all embedded services: ```env MAESTER_CPU_SAFE_MODE=true MAESTER_MOUNT_SERVICES=true MAESTER_ENABLE_KTTS=true MAESTER_ENABLE_MUSICGEN=true MAESTER_ENABLE_WHISPER=true MAESTER_ENABLE_FFMPEG_AUTOMATION=true MAESTER_ENABLE_RENDER_ENGINE=true CUDA_VISIBLE_DEVICES= WHISPER_DEVICE=cpu WHISPER_COMPUTE_TYPE=int8 MAX_RENDER_WORKERS=1 ``` This mode runs the full gateway plus all mounted services while constraining runtime to CPU. MusicGen and Whisper can still be slow on free CPU hardware because model loading and inference are inherently heavy; the defaults keep rendering to one worker, force Whisper CPU/int8, hide CUDA devices, and limit common thread pools. For Hugging Face Spaces, set these as Space secrets before exposing the app: ```env MAESTER_API_KEY=your-long-api-key AVA2LON_SIGNING_SECRET=your-long-render-secret BASYX_SIGNING_SECRET=your-long-render-secret ``` For a lighter control-plane-only deployment, use [Dockerfile.gateway](Dockerfile.gateway). [Dockerfile.full](Dockerfile.full) is kept as an explicit full-stack alias. ## Local Development Python is not available in the current Termux environment, but on a machine with Python: ```bash python -m venv .venv . .venv/bin/activate pip install -r requirements.txt MAESTER_ENV=development MAESTER_ALLOW_DEV_NO_API_KEY=true uvicorn maester_enterprise.main:app --reload ``` ## Production Notes - Set `MAESTER_API_KEY` before exposing the app. - Set `AVA2LON_SIGNING_SECRET` and `BASYX_SIGNING_SECRET`; do not use defaults. - Keep `ALLOW_PRIVATE_ASSET_URLS=false` unless the deployment is isolated and trusted. - Disable unused services with `MAESTER_ENABLE_=false` only if you want a smaller/faster deployment. - Put this app behind TLS and a real edge proxy for internet exposure.