From 145e341c5c32c68d651930f6d9f0a22cdb4f92eb Mon Sep 17 00:00:00 2001 From: Sidney Swift <158200036+sidneyswift@users.noreply.github.com> Date: Fri, 3 Apr 2026 11:45:39 -0400 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20content=20V2=20docs=20=E2=80=94=20m?= =?UTF-8?q?alleable-first=20architecture,=20templates=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OpenAPI: move PATCH edit from /video to /content, add image_url input - OpenAPI: ContentTemplate schema — rename name→id, drop defaultLipsync - OpenAPI: add GET /api/content/templates/{id} detail endpoint - OpenAPI: add template field to video, image, text primitive schemas - OpenAPI: make pipeline template optional (remove default) - OpenAPI: remove V1 artifacts (era_config, pipeline_config) - Guide: new content.mdx — primitives, templates, override priority, video modes - Guide: update content-agent.mdx — template is optional - Nav: add Content group to Guides tab Made-with: Cursor --- api-reference/content/edit.mdx | 2 +- api-reference/content/template-detail.mdx | 4 + api-reference/openapi.json | 893 +++++++++++++++++++--- content-agent.mdx | 2 +- content.mdx | 122 +++ docs.json | 7 + 6 files changed, 924 insertions(+), 106 deletions(-) create mode 100644 api-reference/content/template-detail.mdx create mode 100644 content.mdx diff --git a/api-reference/content/edit.mdx b/api-reference/content/edit.mdx index 8375434..e70820d 100644 --- a/api-reference/content/edit.mdx +++ b/api-reference/content/edit.mdx @@ -1,4 +1,4 @@ --- title: 'Edit Content' -openapi: 'PATCH /api/content/video' +openapi: 'PATCH /api/content' --- diff --git a/api-reference/content/template-detail.mdx b/api-reference/content/template-detail.mdx new file mode 100644 index 0000000..a7daf1a --- /dev/null +++ b/api-reference/content/template-detail.mdx @@ -0,0 +1,4 @@ +--- +title: 'Get Template Detail' +openapi: 'GET /api/content/templates/{id}' +--- diff --git a/api-reference/openapi.json b/api-reference/openapi.json index f7999eb..9a23d78 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -4041,6 +4041,72 @@ } } }, + "/api/content": { + "patch": { + "description": "Apply edits to content — trim, crop, resize, overlay text, or add an audio track. Accepts video, image, or audio input. Pass a `template` for a preset edit pipeline, or build your own with an `operations` array.", + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "description": "Edit parameters", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateEditRequest" + } + } + } + }, + "responses": { + "202": { + "description": "Edit task triggered successfully. Poll via [GET /api/tasks/runs](/api-reference/tasks/runs) using the returned `runId`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateEditResponse" + } + } + } + }, + "400": { + "description": "Validation failed — invalid or missing request body fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — invalid or missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + } + } + } + }, "/api/content/create": { "post": { "description": "Trigger the content creation pipeline for an artist. Provide `artist_account_id` to identify the target artist. Validates the artist has all required files (face guide, songs) unless overridden via `songs` URLs or `images`, then triggers a background task that generates a short-form video. Returns `runIds` — an array of run IDs that can each be polled via [GET /api/tasks/runs](/api-reference/tasks/runs).", @@ -4109,7 +4175,7 @@ }, "/api/content/templates": { "get": { - "description": "List all available content creation templates. Templates define the visual style, scene composition, and default workflow settings for generated videos. Each template includes reference images, style guides, caption rules, and prompt configurations.", + "description": "List all available content creation templates. Templates are optional — every content primitive works without one. When you do use a template, it provides a complete creative recipe: image prompts, video motion config, caption style rules, and edit operations. Returns template ID and description only — enough to pick the right one.", "security": [ { "apiKeyAuth": [] @@ -4142,6 +4208,73 @@ } } }, + "/api/content/templates/{id}": { + "get": { + "description": "Get the full configuration for a specific content creation template. Returns the complete creative recipe including image prompts, video motion config, caption style rules, and edit operations.", + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Template identifier", + "required": true, + "schema": { + "type": "string", + "example": "artist-caption-bedroom" + } + } + ], + "responses": { + "200": { + "description": "Template detail retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentTemplateDetail" + } + } + } + }, + "401": { + "description": "Unauthorized — invalid or missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "404": { + "description": "Template not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "error" + }, + "error": { + "type": "string", + "example": "Template not found" + } + } + } + } + } + } + } + } + }, "/api/content/validate": { "get": { "description": "Check whether an artist has all the required files to run the content creation pipeline. Returns a structured report of each required and recommended file with its status. Required files must be present or the pipeline will fail. Recommended files improve output quality but are not strictly necessary. Provide `artist_account_id` as a query parameter.", @@ -5435,24 +5568,354 @@ } } } - }, - "400": { - "description": "Invalid request body" - }, - "401": { - "description": "Missing or invalid callback secret" - } - }, - "security": [ - { - "callbackSecret": [] + }, + "400": { + "description": "Invalid request body" + }, + "401": { + "description": "Missing or invalid callback secret" + } + }, + "security": [ + { + "callbackSecret": [] + } + ] + } + }, + "/api/content/upscale": { + "post": { + "description": "Upscale an image or video to higher resolution. Pass the URL and specify the type. Returns the upscaled URL.", + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "description": "Upscale parameters", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateUpscaleRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Media upscaled successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateUpscaleResponse" + } + } + } + }, + "400": { + "description": "Validation failed — invalid or missing request body fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — invalid or missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + } + } + } + }, + "/api/content/analyze": { + "post": { + "description": "Analyze a video and answer questions about it. Pass a video URL and a text prompt — for example, \"Describe what happens\" or \"Rate the visual quality 1-10.\" Returns the generated text.", + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "description": "Video analysis parameters", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateAnalyzeRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Video analyzed successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateAnalyzeResponse" + } + } + } + }, + "400": { + "description": "Validation failed — invalid or missing request body fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — invalid or missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "502": { + "description": "Video analysis failed upstream", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + } + } + } + }, + "/api/content/caption": { + "post": { + "description": "Generate a short caption from a topic. Returns the text content and default styling (font, color, size).", + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "description": "Text generation parameters", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateTextRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Text generated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateTextResponse" + } + } + } + }, + "400": { + "description": "Validation failed — invalid or missing request body fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — invalid or missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + } + } + } + }, + "/api/content/image": { + "post": { + "description": "Generate an image from a text prompt. Pass a `reference_image_url` to guide the output toward a specific look or subject. Returns the image URL.", + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "description": "Image generation parameters", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateImageRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Image generated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateImageResponse" + } + } + } + }, + "400": { + "description": "Validation failed — invalid or missing request body fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — invalid or missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + } + } + } + }, + "/api/content/video": { + "post": { + "description": "Generate a video. Set `mode` to control what kind of video you get:\n\n- `prompt` — create a video from a text description\n- `animate` — animate a still image\n- `reference` — use an image as a style/subject reference (not the first frame)\n- `extend` — continue an existing video\n- `first-last` — generate a video that transitions between two images\n- `lipsync` — sync face movement to an audio clip\n\nIf `mode` is omitted, it's inferred from the inputs you provide.", + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "description": "Video generation parameters", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateVideoRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Video generated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentCreateVideoResponse" + } + } + } + }, + "400": { + "description": "Validation failed — invalid or missing request body fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized — invalid or missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentErrorResponse" + } + } + } } - ] + } } }, - "/api/content/upscale": { + "/api/content/transcribe": { "post": { - "description": "Upscale an image or video to higher resolution. Pass the URL and specify the type. Returns the upscaled URL.", + "description": "Transcribe audio into text with word-level timestamps. Pass one or more audio file URLs in `audio_urls`. Returns the full transcript and an array of timed segments.", "security": [ { "apiKeyAuth": [] @@ -5462,23 +5925,23 @@ } ], "requestBody": { - "description": "Upscale parameters", + "description": "Audio transcription parameters", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentCreateUpscaleRequest" + "$ref": "#/components/schemas/ContentCreateAudioRequest" } } } }, "responses": { "200": { - "description": "Media upscaled successfully", + "description": "Song transcribed successfully", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentCreateUpscaleResponse" + "$ref": "#/components/schemas/ContentCreateAudioResponse" } } } @@ -5516,9 +5979,9 @@ } } }, - "/api/content/analyze": { + "/api/music/compose": { "post": { - "description": "Analyze a video and answer questions about it. Pass a video URL and a text prompt — for example, \"Describe what happens\" or \"Rate the visual quality 1-10.\" Returns the generated text.", + "description": "Generate a song from a text prompt or a composition plan. The prompt can describe mood, genre, instruments, lyrics, and structure. Alternatively, pass a full composition_plan for fine-grained control over sections, styles, and lyrics. Returns binary audio in the requested format (default MP3).", "security": [ { "apiKeyAuth": [] @@ -5528,23 +5991,32 @@ } ], "requestBody": { - "description": "Video analysis parameters", + "description": "Music composition parameters", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentCreateAnalyzeRequest" + "$ref": "#/components/schemas/MusicComposeRequest" } } } }, "responses": { "200": { - "description": "Video analyzed successfully", + "description": "Audio generated successfully", + "headers": { + "song-id": { + "schema": { + "type": "string" + }, + "description": "Unique identifier for the generated song" + } + }, "content": { - "application/json": { + "audio/mpeg": { "schema": { - "$ref": "#/components/schemas/ContentCreateAnalyzeResponse" + "type": "string", + "format": "binary" } } } @@ -5554,7 +6026,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5564,17 +6036,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } }, - "502": { - "description": "Video analysis failed upstream", + "500": { + "description": "Internal server error", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5582,9 +6054,9 @@ } } }, - "/api/content/caption": { + "/api/music/compose/detailed": { "post": { - "description": "Generate a short caption from a topic. Returns the text content and default styling (font, color, size).", + "description": "Generate a song with metadata and optional word-level timestamps. Returns a multipart/mixed response: the first part is JSON metadata (song ID, duration, seed, etc.) and the second part is binary audio. Use with_timestamps to get precise timing for each word in the lyrics.", "security": [ { "apiKeyAuth": [] @@ -5594,23 +6066,24 @@ } ], "requestBody": { - "description": "Text generation parameters", + "description": "Music composition parameters with optional timestamps", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentCreateTextRequest" + "$ref": "#/components/schemas/MusicComposeDetailedRequest" } } } }, "responses": { "200": { - "description": "Text generated successfully", + "description": "Audio and metadata generated successfully. Response is multipart/mixed: JSON metadata followed by binary audio.", "content": { - "application/json": { + "multipart/mixed": { "schema": { - "$ref": "#/components/schemas/ContentCreateTextResponse" + "type": "string", + "format": "binary" } } } @@ -5620,7 +6093,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5630,7 +6103,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5640,7 +6113,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5648,9 +6121,9 @@ } } }, - "/api/content/image": { + "/api/music/stream": { "post": { - "description": "Generate an image from a text prompt. Pass a `reference_image_url` to guide the output toward a specific look or subject. Returns the image URL.", + "description": "Generate a song and stream audio chunks in real time. Use this endpoint when you want to begin playback before the full song is generated. The response streams binary audio chunks as they are produced.", "security": [ { "apiKeyAuth": [] @@ -5660,23 +6133,32 @@ } ], "requestBody": { - "description": "Image generation parameters", + "description": "Music streaming parameters", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentCreateImageRequest" + "$ref": "#/components/schemas/MusicStreamRequest" } } } }, "responses": { "200": { - "description": "Image generated successfully", + "description": "Audio stream started successfully", + "headers": { + "song-id": { + "schema": { + "type": "string" + }, + "description": "Unique identifier for the generated song" + } + }, "content": { - "application/json": { + "audio/mpeg": { "schema": { - "$ref": "#/components/schemas/ContentCreateImageResponse" + "type": "string", + "format": "binary" } } } @@ -5686,7 +6168,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5696,7 +6178,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5706,7 +6188,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5714,9 +6196,9 @@ } } }, - "/api/content/video": { + "/api/music/plan": { "post": { - "description": "Generate a video. Set `mode` to control what kind of video you get:\n\n- `prompt` — create a video from a text description\n- `animate` — animate a still image\n- `reference` — use an image as a style/subject reference (not the first frame)\n- `extend` — continue an existing video\n- `first-last` — generate a video that transitions between two images\n- `lipsync` — sync face movement to an audio clip\n\nIf `mode` is omitted, it's inferred from the inputs you provide.", + "description": "Create a composition plan from a text prompt. A composition plan is a structured representation of a song — sections, styles, lyrics, and durations — that you can review and tweak before passing to the compose endpoint. This endpoint is free and does not consume credits.", "security": [ { "apiKeyAuth": [] @@ -5726,23 +6208,23 @@ } ], "requestBody": { - "description": "Video generation parameters", + "description": "Plan creation parameters", "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentCreateVideoRequest" + "$ref": "#/components/schemas/MusicCreatePlanRequest" } } } }, "responses": { "200": { - "description": "Video generated successfully", + "description": "Composition plan created successfully", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentCreateVideoResponse" + "$ref": "#/components/schemas/MusicCreatePlanResponse" } } } @@ -5752,7 +6234,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5762,7 +6244,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5772,15 +6254,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } } } - }, - "patch": { - "description": "Apply edits to a video or audio file — trim, crop, resize, overlay text, or add an audio track. Pass a `template` for a preset edit pipeline, or build your own with an `operations` array. Everything runs in one pass.", + } + }, + "/api/music/video-to-music": { + "post": { + "description": "Generate background music from video files. Upload 1–10 video files via multipart/form-data (max 200 MB total). The AI analyzes the video content and generates music that matches the mood, pacing, and style. Optionally provide a text description and style tags to guide the output.", "security": [ { "apiKeyAuth": [] @@ -5790,23 +6274,67 @@ } ], "requestBody": { - "description": "Edit parameters", + "description": "Video files and optional generation parameters", "required": true, "content": { - "application/json": { + "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ContentCreateEditRequest" + "type": "object", + "properties": { + "files": { + "type": "array", + "items": { + "type": "string", + "format": "binary" + }, + "minItems": 1, + "maxItems": 10, + "description": "Video files to analyze (max 200 MB total)" + }, + "description": { + "type": "string", + "maxLength": 1000, + "description": "Optional text description to guide the music generation" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 10, + "description": "Optional style tags to influence the output (max 10)" + }, + "sign_with_c2pa": { + "type": "boolean", + "default": false, + "description": "Sign the output with C2PA content credentials" + }, + "output_format": { + "type": "string", + "description": "Audio output format (e.g. mp3_44100_192)" + } + }, + "required": ["files"] } } } }, "responses": { - "202": { - "description": "Edit task triggered successfully. Poll via [GET /api/tasks/runs](/api-reference/tasks/runs) using the returned `runId`.", + "200": { + "description": "Background music generated successfully", + "headers": { + "song-id": { + "schema": { + "type": "string" + }, + "description": "Unique identifier for the generated song" + } + }, "content": { - "application/json": { + "audio/mpeg": { "schema": { - "$ref": "#/components/schemas/ContentCreateEditResponse" + "type": "string", + "format": "binary" } } } @@ -5816,7 +6344,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5826,7 +6354,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5836,7 +6364,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5844,9 +6372,9 @@ } } }, - "/api/content/transcribe": { + "/api/music/stem-separation": { "post": { - "description": "Transcribe audio into text with word-level timestamps. Pass one or more audio file URLs in `audio_urls`. Returns the full transcript and an array of timed segments.", + "description": "Separate an audio file into individual stems (vocals, drums, bass, etc.). Upload an audio file via multipart/form-data. Choose between two-stem separation (vocals + instrumental) or six-stem separation (vocals, drums, bass, guitar, piano, other). Returns a ZIP archive containing the separated stems.", "security": [ { "apiKeyAuth": [] @@ -5856,23 +6384,47 @@ } ], "requestBody": { - "description": "Audio transcription parameters", + "description": "Audio file and separation parameters", "required": true, "content": { - "application/json": { + "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ContentCreateAudioRequest" + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "Audio file to separate into stems" + }, + "stem_variation_id": { + "type": "string", + "enum": ["two_stems_v1", "six_stems_v1"], + "default": "six_stems_v1", + "description": "Stem separation mode. `two_stems_v1` produces vocals + instrumental. `six_stems_v1` produces vocals, drums, bass, guitar, piano, and other." + }, + "sign_with_c2pa": { + "type": "boolean", + "default": false, + "description": "Sign the output with C2PA content credentials" + }, + "output_format": { + "type": "string", + "description": "Audio output format (e.g. mp3_44100_192)" + } + }, + "required": ["file"] } } } }, "responses": { "200": { - "description": "Song transcribed successfully", + "description": "Stems separated successfully. Returns a ZIP archive containing the individual stems.", "content": { - "application/json": { + "application/zip": { "schema": { - "$ref": "#/components/schemas/ContentCreateAudioResponse" + "type": "string", + "format": "binary" } } } @@ -5882,7 +6434,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5892,7 +6444,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -5902,7 +6454,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" + "$ref": "#/components/schemas/MusicErrorResponse" } } } @@ -11754,9 +12306,8 @@ }, "template": { "type": "string", - "description": "The template to use for content generation. Defines the visual style, scene, and prompt configuration. If omitted, defaults to `artist-caption-bedroom`. See [GET /api/content/templates](/api-reference/content/templates) for available options.", - "example": "artist-caption-stage", - "default": "artist-caption-bedroom" + "description": "Optional template ID for content generation. Defines the visual style, scene, and prompt configuration. When omitted, the pipeline runs in malleable mode using only the params you provide. See GET /api/content/templates for available options.", + "example": "artist-caption-stage" }, "lipsync": { "type": "boolean", @@ -11934,26 +12485,148 @@ "ContentTemplate": { "type": "object", "required": [ - "name", - "description", - "defaultLipsync" + "id", + "description" ], - "description": "A content creation template that defines the visual style and composition for generated videos.", + "description": "A content creation template — a complete creative recipe defining visual style, composition, caption rules, and edit operations. Templates are optional; all primitives work without one.", "properties": { - "name": { + "id": { "type": "string", - "description": "Template identifier. Pass this as the `template` field in [POST /api/content/create](/api-reference/content/create).", + "description": "Template identifier. Pass this as the template field in content primitive requests or POST /api/content/create.", "example": "artist-caption-bedroom" }, "description": { "type": "string", "description": "Human-readable description of the template's visual style", - "example": "Moody purple bedroom setting with caption overlay" + "example": "Moody bedroom selfie. Artist on camera with deadpan expression, purple LED lighting, dark room. Short blunt captions in lowercase. Vertical 9:16 video, 8 seconds. Best for: introspective songs, vulnerable moments, daily content. Requires: face image, audio." + } + } + }, + "ContentTemplateDetail": { + "type": "object", + "required": [ + "id", + "description" + ], + "description": "Full configuration for a content creation template including image prompts, video motion config, caption style rules, and edit operations.", + "properties": { + "id": { + "type": "string", + "description": "Template identifier", + "example": "artist-caption-bedroom" }, - "defaultLipsync": { - "type": "boolean", - "description": "Whether this template defaults to lip-sync mode when the `lipsync` flag is not explicitly set", - "example": false + "description": { + "type": "string", + "description": "Human-readable description of the template", + "example": "Moody bedroom selfie. Artist on camera with deadpan expression, purple LED lighting, dark room." + }, + "image": { + "type": "object", + "description": "Image generation configuration", + "properties": { + "prompt": { + "type": "string", + "description": "Default image prompt used when no caller prompt is provided" + }, + "reference_images": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + }, + "description": "Reference image URLs for style conditioning" + }, + "style_rules": { + "type": "object", + "description": "Style constraints (lighting, colors, composition)" + } + } + }, + "video": { + "type": "object", + "description": "Video generation configuration", + "properties": { + "moods": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Mood descriptors for motion prompt generation" + }, + "movements": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Camera/subject movement descriptors" + } + } + }, + "caption": { + "type": "object", + "description": "Caption generation configuration", + "properties": { + "guide": { + "type": "object", + "description": "Caption style guide", + "properties": { + "tone": { + "type": "string", + "description": "Voice and tone direction" + }, + "rules": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Style rules for caption generation" + }, + "formats": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Allowed caption formats" + } + } + }, + "examples": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Example captions for few-shot prompting" + } + } + }, + "edit": { + "type": "object", + "description": "Post-processing edit operations", + "properties": { + "operations": { + "type": "array", + "description": "Default edit operations applied during post-processing", + "items": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "trim", + "crop", + "resize", + "overlay_text", + "mux_audio" + ], + "description": "Operation type" + } + } + } + } + } } } }, @@ -12006,12 +12679,6 @@ "audience_context": { "$ref": "#/components/schemas/ContentValidationCheck" }, - "era_config": { - "$ref": "#/components/schemas/ContentValidationCheck" - }, - "pipeline_config": { - "$ref": "#/components/schemas/ContentValidationCheck" - }, "songs": { "allOf": [ { @@ -12712,6 +13379,10 @@ "ContentCreateImageRequest": { "type": "object", "properties": { + "template": { + "type": "string", + "description": "Optional template ID. When provided, uses the template's image prompt, reference images, and style rules. Caller params override template defaults. Artist context overrides template style when available." + }, "prompt": { "type": "string", "description": "Optional prompt to guide image generation" @@ -12751,6 +13422,10 @@ "ContentCreateVideoRequest": { "type": "object", "properties": { + "template": { + "type": "string", + "description": "Optional template ID. When provided, the template's video config (moods, movements) is used as the motion prompt. Caller params override template defaults." + }, "mode": { "type": "string", "enum": [ @@ -12852,6 +13527,10 @@ "topic" ], "properties": { + "template": { + "type": "string", + "description": "Optional template ID. When provided, injects the template's caption guide (tone, rules, formats) and examples into the LLM prompt. Caller's topic is still required." + }, "topic": { "type": "string", "description": "The subject or theme for caption generation" @@ -12980,12 +13659,18 @@ }, "ContentCreateEditRequest": { "type": "object", + "description": "Must provide at least one input (video_url, image_url, or audio_url)", "properties": { "video_url": { "type": "string", "format": "uri", "description": "Input video URL" }, + "image_url": { + "type": "string", + "format": "uri", + "description": "Input image URL" + }, "audio_url": { "type": "string", "format": "uri", diff --git a/content-agent.mdx b/content-agent.mdx index 10d4f89..0926ebb 100644 --- a/content-agent.mdx +++ b/content-agent.mdx @@ -32,7 +32,7 @@ Bot replies in-thread with the generated video URL(s) | Parameter | Required | Description | |-----------|----------|-------------| | `artist_account_id` | Yes | UUID of the artist account | -| `template` | No | Content template name (defaults to `artist-caption-bedroom`) | +| `template` | No | Content template name. Optional — when omitted, the pipeline runs with default settings. See [GET /api/content/templates](/api-reference/content/templates) for options. | | `batch=N` | No | Number of videos to generate (1-30, default 1) | | `lipsync` | No | Enable lipsync mode (audio baked into video) | diff --git a/content.mdx b/content.mdx new file mode 100644 index 0000000..c427883 --- /dev/null +++ b/content.mdx @@ -0,0 +1,122 @@ +--- +title: 'Content Creation' +description: 'Generate images, videos, captions, and social-ready content using AI-powered primitives' +--- + +## Overview + +Recoup's content API gives you seven independent primitives for generating and editing visual content. Each primitive does one thing well. You orchestrate them. + +**Every primitive works without a template.** Pass your own prompt, reference images, and parameters directly. Templates are optional shortcuts — opinionated creative recipes that pre-fill parameters for a specific look. + +## Primitives + +| Primitive | Endpoint | What it does | +|-----------|----------|-------------| +| Generate Image | [POST /api/content/image](/api-reference/content/generate-image) | Create an image from a text prompt, optionally with a reference image for face/style | +| Generate Video | [POST /api/content/video](/api-reference/content/generate-video) | Create a video — 6 modes: prompt, animate, reference, extend, first-last, lipsync | +| Generate Caption | [POST /api/content/caption](/api-reference/content/generate-caption) | Generate on-screen text for social media videos | +| Transcribe Audio | [POST /api/content/transcribe](/api-reference/content/transcribe-audio) | Transcribe audio to timestamped lyrics/text | +| Edit Content | [PATCH /api/content](/api-reference/content/edit) | Trim, crop, resize, overlay text, or add audio — one processing pass | +| Upscale | [POST /api/content/upscale](/api-reference/content/upscale) | Upscale image or video resolution (up to 4x) | +| Analyze Video | [POST /api/content/analyze](/api-reference/content/analyze-video) | AI video analysis — describe scenes, check quality, evaluate content | + +There is also [POST /api/content/create](/api-reference/content/create) which runs the full pipeline in one call — use it when you want a video without creative control over each step. + +## How It Works + +### Without a template (malleable mode) + +Pass your own parameters directly to any primitive. Maximum creative control. + +```bash +# Generate an image with your own prompt +curl -X POST https://recoup-api.vercel.app/api/content/image \ + -H "x-api-key: YOUR_KEY" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "A moody portrait in a dimly lit room, front-facing phone camera"}' + +# Generate a video from that image +curl -X POST https://recoup-api.vercel.app/api/content/video \ + -H "x-api-key: YOUR_KEY" \ + -H "Content-Type: application/json" \ + -d '{"image_url": "IMAGE_URL_FROM_ABOVE", "prompt": "subtle breathing motion, nearly still"}' +``` + +### With a template (shortcut mode) + +Pass a template ID and the primitive fills in prompts, reference images, and style rules automatically. You can still override any parameter. + +```bash +# Same image, but the template provides the prompt and reference images +curl -X POST https://recoup-api.vercel.app/api/content/image \ + -H "x-api-key: YOUR_KEY" \ + -H "Content-Type: application/json" \ + -d '{"template": "artist-caption-bedroom", "reference_image_url": "YOUR_FACE_IMAGE"}' +``` + +Use [GET /api/content/templates](/api-reference/content/templates) to see available templates with descriptions. + +## Templates + +A template is a complete creative recipe — it defines what a piece of content looks like across every primitive: + +- **Image config**: prompt, reference images, style rules (camera, lighting, composition) +- **Video config**: mood variations, movement descriptions +- **Caption config**: tone, formatting rules, example captions +- **Edit config**: crop ratio, text overlay style, audio mixing + +Templates are optional. They save time by pre-filling parameters with curated defaults. When you see customers repeatedly creating the same kind of content, that pattern becomes a template. + +### Override priority + +When using a template, your explicit parameters always win: + +1. **Your params** — highest priority. What you pass overrides everything. +2. **Artist context** — if the artist has a style guide, it personalizes the template. +3. **Template defaults** — lowest priority. The recipe's built-in values. + +## Architecture + +``` +┌──────────────────────────────────────────────┐ +│ SKILL (advisory) │ +│ Teaches the agent which template to pick, │ +│ when to override, when to skip templates │ +├──────────────────────────────────────────────┤ +│ TEMPLATE (optional, enforced when used) │ +│ Complete creative recipe — image + video + │ +│ caption + edit config. Applied server-side. │ +├──────────────────────────────────────────────┤ +│ PRIMITIVES (execution) │ +│ image, video, caption, transcribe, edit, │ +│ upscale, analyze. Work with or without │ +│ templates. Do exactly what they're told. │ +└──────────────────────────────────────────────┘ +``` + +## Video Modes + +The video primitive supports 6 generation modes: + +| Mode | What it does | Required inputs | +|------|-------------|-----------------| +| `prompt` | Create from text description | `prompt` | +| `animate` | Animate a still image | `image_url`, `prompt` | +| `reference` | Use image as style reference (not first frame) | `image_url`, `prompt` | +| `extend` | Continue an existing video | `video_url`, `prompt` | +| `first-last` | Transition between two images | `image_url`, `end_image_url`, `prompt` | +| `lipsync` | Sync face to audio | `image_url`, `audio_url` | + +Set `mode` explicitly, or omit it and the API infers the mode from the inputs you provide. + +## Iteration + +Each primitive is independent. Redo any step without rerunning the whole pipeline: + +- Bad image? Regenerate with a different prompt or reference +- Caption too long? Regenerate with `length: "short"` +- Video glitchy? Analyze it, then regenerate with adjusted params +- Clip too short? Use `extend` mode to continue it +- Low quality? Upscale the image or video +- Everything good but wrong caption? Just re-run the edit step diff --git a/docs.json b/docs.json index 9639f6b..c0169fd 100644 --- a/docs.json +++ b/docs.json @@ -28,6 +28,12 @@ "pages": [ "content-agent" ] + }, + { + "group": "Content", + "pages": [ + "content" + ] } ] }, @@ -255,6 +261,7 @@ "api-reference/content/upscale", "api-reference/content/analyze-video", "api-reference/content/templates", + "api-reference/content/template-detail", "api-reference/content/validate", "api-reference/content/estimate" ] From d35aabfc59f5238d9f8440409ceacb10fbdae9ad Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Thu, 9 Apr 2026 07:06:36 -0500 Subject: [PATCH 2/4] docs: link template params to List Templates endpoint Add cross-reference links to GET /api/content/templates in all template parameter descriptions so devs and AI can easily discover available options. Co-Authored-By: Claude Opus 4.6 (1M context) --- api-reference/openapi.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api-reference/openapi.json b/api-reference/openapi.json index c511880..bc8c9b5 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -14565,7 +14565,7 @@ "properties": { "template": { "type": "string", - "description": "Optional template ID. When provided, uses the template's image prompt, reference images, and style rules. Caller params override template defaults. Artist context overrides template style when available." + "description": "Optional template ID. When provided, uses the template's image prompt, reference images, and style rules. Caller params override template defaults. Artist context overrides template style when available. See [GET /api/content/templates](/api-reference/content/templates) for available options." }, "prompt": { "type": "string", @@ -14608,7 +14608,7 @@ "properties": { "template": { "type": "string", - "description": "Optional template ID. When provided, the template's video config (moods, movements) is used as the motion prompt. Caller params override template defaults." + "description": "Optional template ID. When provided, the template's video config (moods, movements) is used as the motion prompt. Caller params override template defaults. See [GET /api/content/templates](/api-reference/content/templates) for available options." }, "mode": { "type": "string", @@ -14713,7 +14713,7 @@ "properties": { "template": { "type": "string", - "description": "Optional template ID. When provided, injects the template's caption guide (tone, rules, formats) and examples into the LLM prompt. Caller's topic is still required." + "description": "Optional template ID. When provided, injects the template's caption guide (tone, rules, formats) and examples into the LLM prompt. Caller's topic is still required. See [GET /api/content/templates](/api-reference/content/templates) for available options." }, "topic": { "type": "string", @@ -14862,7 +14862,7 @@ }, "template": { "type": "string", - "description": "Template name for deterministic edit config. If provided, operations are read from the template." + "description": "Template name for deterministic edit config. If provided, operations are read from the template. See [GET /api/content/templates](/api-reference/content/templates) for available options." }, "operations": { "type": "array", From 6a01f2555865bd6612e1821380712c273009264b Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Thu, 9 Apr 2026 07:16:42 -0500 Subject: [PATCH 3/4] docs: merge content.mdx into content-agent, rename to Content Consolidated the orphaned content.mdx (primitives, templates, video modes, iteration guide) into content-agent.mdx and renamed the page title to "Content". Deleted content.mdx since it wasn't in the navigation. Co-Authored-By: Claude Opus 4.6 (1M context) --- content-agent.mdx | 134 +++++++++++++++++++++++++++++++++++----------- content.mdx | 122 ----------------------------------------- 2 files changed, 103 insertions(+), 153 deletions(-) delete mode 100644 content.mdx diff --git a/content-agent.mdx b/content-agent.mdx index 0926ebb..4f78a55 100644 --- a/content-agent.mdx +++ b/content-agent.mdx @@ -1,28 +1,111 @@ --- -title: 'Content Agent' -description: 'Generate artist videos from Slack with the Recoup Content Agent bot' +title: 'Content' +description: 'Generate images, videos, captions, and social-ready content using AI-powered primitives' --- ## Overview -The **Recoup Content Agent** is a Slack bot that generates social-ready artist videos on @mention. It plugs into the existing [content creation pipeline](/api-reference/content/create) and delivers results directly in your Slack thread. +Recoup's content API gives you seven independent primitives for generating and editing visual content. Each primitive does one thing well. You orchestrate them. + +**Every primitive works without a template.** Pass your own prompt, reference images, and parameters directly. Templates are optional shortcuts — opinionated creative recipes that pre-fill parameters for a specific look. + +## Primitives + +| Primitive | Endpoint | What it does | +|-----------|----------|-------------| +| Generate Image | [POST /api/content/image](/api-reference/content/generate-image) | Create an image from a text prompt, optionally with a reference image for face/style | +| Generate Video | [POST /api/content/video](/api-reference/content/generate-video) | Create a video — 6 modes: prompt, animate, reference, extend, first-last, lipsync | +| Generate Caption | [POST /api/content/caption](/api-reference/content/generate-caption) | Generate on-screen text for social media videos | +| Transcribe Audio | [POST /api/content/transcribe](/api-reference/content/transcribe-audio) | Transcribe audio to timestamped lyrics/text | +| Edit Content | [PATCH /api/content](/api-reference/content/edit) | Trim, crop, resize, overlay text, or add audio — one processing pass | +| Upscale | [POST /api/content/upscale](/api-reference/content/upscale) | Upscale image or video resolution (up to 4x) | +| Analyze Video | [POST /api/content/analyze](/api-reference/content/analyze-video) | AI video analysis — describe scenes, check quality, evaluate content | + +There is also [POST /api/content/create](/api-reference/content/create) which runs the full pipeline in one call — use it when you want a video without creative control over each step. ## How It Works +### Without a template (malleable mode) + +Pass your own parameters directly to any primitive. Maximum creative control. + +```bash +# Generate an image with your own prompt +curl -X POST https://recoup-api.vercel.app/api/content/image \ + -H "x-api-key: YOUR_KEY" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "A moody portrait in a dimly lit room, front-facing phone camera"}' + +# Generate a video from that image +curl -X POST https://recoup-api.vercel.app/api/content/video \ + -H "x-api-key: YOUR_KEY" \ + -H "Content-Type: application/json" \ + -d '{"image_url": "IMAGE_URL_FROM_ABOVE", "prompt": "subtle breathing motion, nearly still"}' ``` -User mentions bot in Slack - ↓ -@RecoupContentAgent [template] [batch=N] [lipsync] - ↓ -Bot validates the artist and sends an immediate acknowledgment - ↓ -Triggers the content creation pipeline (POST /api/content/create) - ↓ -Background task polls for completion (~5-10 min) - ↓ -Bot replies in-thread with the generated video URL(s) + +### With a template (shortcut mode) + +Pass a template ID and the primitive fills in prompts, reference images, and style rules automatically. You can still override any parameter. + +```bash +# Same image, but the template provides the prompt and reference images +curl -X POST https://recoup-api.vercel.app/api/content/image \ + -H "x-api-key: YOUR_KEY" \ + -H "Content-Type: application/json" \ + -d '{"template": "artist-caption-bedroom", "reference_image_url": "YOUR_FACE_IMAGE"}' ``` +Use [GET /api/content/templates](/api-reference/content/templates) to see available templates with descriptions. + +## Templates + +A template is a complete creative recipe — it defines what a piece of content looks like across every primitive: + +- **Image config**: prompt, reference images, style rules (camera, lighting, composition) +- **Video config**: mood variations, movement descriptions +- **Caption config**: tone, formatting rules, example captions +- **Edit config**: crop ratio, text overlay style, audio mixing + +Templates are optional. They save time by pre-filling parameters with curated defaults. When you see customers repeatedly creating the same kind of content, that pattern becomes a template. + +### Override priority + +When using a template, your explicit parameters always win: + +1. **Your params** — highest priority. What you pass overrides everything. +2. **Artist context** — if the artist has a style guide, it personalizes the template. +3. **Template defaults** — lowest priority. The recipe's built-in values. + +## Video Modes + +The video primitive supports 6 generation modes: + +| Mode | What it does | Required inputs | +|------|-------------|-----------------| +| `prompt` | Create from text description | `prompt` | +| `animate` | Animate a still image | `image_url`, `prompt` | +| `reference` | Use image as style reference (not first frame) | `image_url`, `prompt` | +| `extend` | Continue an existing video | `video_url`, `prompt` | +| `first-last` | Transition between two images | `image_url`, `end_image_url`, `prompt` | +| `lipsync` | Sync face to audio | `image_url`, `audio_url` | + +Set `mode` explicitly, or omit it and the API infers the mode from the inputs you provide. + +## Iteration + +Each primitive is independent. Redo any step without rerunning the whole pipeline: + +- Bad image? Regenerate with a different prompt or reference +- Caption too long? Regenerate with `length: "short"` +- Video glitchy? Analyze it, then regenerate with adjusted params +- Clip too short? Use `extend` mode to continue it +- Low quality? Upscale the image or video +- Everything good but wrong caption? Just re-run the edit step + +## Content Agent (Slack Bot) + +The **Recoup Content Agent** is a Slack bot that generates social-ready artist videos on @mention. It plugs into the content creation pipeline and delivers results directly in your Slack thread. + ### @Mention Syntax ``` @@ -53,9 +136,7 @@ Bot replies in-thread with the generated video URL(s) @RecoupContentAgent abc-123-uuid batch=3 lipsync ``` -## Architecture - -The content agent follows the same pattern as the coding agent bot: +### Architecture | Component | Location | Purpose | |-----------|----------|---------| @@ -73,18 +154,9 @@ The content agent follows the same pattern as the coding agent bot: 4. **Poll task** (`poll-content-run`) monitors the Trigger.dev runs every 30 seconds (up to 30 minutes) 5. **Callback** → [`POST /api/content-agent/callback`](/api-reference/content-agent/callback) receives results and posts video URLs back to the Slack thread -### API Endpoints Used - -| Endpoint | When | Purpose | -|----------|------|---------| -| [`GET /api/content/validate`](/api-reference/content/validate) | Before triggering | Checks if the artist has required assets | -| [`POST /api/content/create`](/api-reference/content/create) | On mention | Triggers the video generation pipeline | -| [`GET /api/content/templates`](/api-reference/content/templates) | Reference | Lists available content templates | -| [`POST /api/content-agent/callback`](/api-reference/content-agent/callback) | After completion | Internal — receives poll results | - -## Setup +### Setup -### 1. Create a Slack App +#### 1. Create a Slack App 1. Go to [api.slack.com/apps](https://api.slack.com/apps) and create a new app 2. Under **OAuth & Permissions**, add bot scopes: @@ -96,7 +168,7 @@ The content agent follows the same pattern as the coding agent bot: - Subscribe to `app_mention` bot event 4. Install the app to your workspace -### 2. Configure Environment Variables +#### 2. Configure Environment Variables | Variable | Where | Description | |----------|-------|-------------| @@ -106,7 +178,7 @@ The content agent follows the same pattern as the coding agent bot: | `RECOUP_API_KEY` | API + Tasks | Recoup API key for authenticating pipeline requests | | `RECOUP_API_BASE_URL` | Tasks (Trigger.dev) | API base URL (e.g., `https://recoup-api.vercel.app`) | -### 3. Verify +#### 3. Verify Mention the bot in any Slack channel where it's been added: @@ -118,7 +190,7 @@ You should see: 1. An immediate acknowledgment message 2. A video URL reply in the thread after ~5-10 minutes -## Troubleshooting +### Troubleshooting | Issue | Cause | Fix | |-------|-------|-----| diff --git a/content.mdx b/content.mdx deleted file mode 100644 index c427883..0000000 --- a/content.mdx +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: 'Content Creation' -description: 'Generate images, videos, captions, and social-ready content using AI-powered primitives' ---- - -## Overview - -Recoup's content API gives you seven independent primitives for generating and editing visual content. Each primitive does one thing well. You orchestrate them. - -**Every primitive works without a template.** Pass your own prompt, reference images, and parameters directly. Templates are optional shortcuts — opinionated creative recipes that pre-fill parameters for a specific look. - -## Primitives - -| Primitive | Endpoint | What it does | -|-----------|----------|-------------| -| Generate Image | [POST /api/content/image](/api-reference/content/generate-image) | Create an image from a text prompt, optionally with a reference image for face/style | -| Generate Video | [POST /api/content/video](/api-reference/content/generate-video) | Create a video — 6 modes: prompt, animate, reference, extend, first-last, lipsync | -| Generate Caption | [POST /api/content/caption](/api-reference/content/generate-caption) | Generate on-screen text for social media videos | -| Transcribe Audio | [POST /api/content/transcribe](/api-reference/content/transcribe-audio) | Transcribe audio to timestamped lyrics/text | -| Edit Content | [PATCH /api/content](/api-reference/content/edit) | Trim, crop, resize, overlay text, or add audio — one processing pass | -| Upscale | [POST /api/content/upscale](/api-reference/content/upscale) | Upscale image or video resolution (up to 4x) | -| Analyze Video | [POST /api/content/analyze](/api-reference/content/analyze-video) | AI video analysis — describe scenes, check quality, evaluate content | - -There is also [POST /api/content/create](/api-reference/content/create) which runs the full pipeline in one call — use it when you want a video without creative control over each step. - -## How It Works - -### Without a template (malleable mode) - -Pass your own parameters directly to any primitive. Maximum creative control. - -```bash -# Generate an image with your own prompt -curl -X POST https://recoup-api.vercel.app/api/content/image \ - -H "x-api-key: YOUR_KEY" \ - -H "Content-Type: application/json" \ - -d '{"prompt": "A moody portrait in a dimly lit room, front-facing phone camera"}' - -# Generate a video from that image -curl -X POST https://recoup-api.vercel.app/api/content/video \ - -H "x-api-key: YOUR_KEY" \ - -H "Content-Type: application/json" \ - -d '{"image_url": "IMAGE_URL_FROM_ABOVE", "prompt": "subtle breathing motion, nearly still"}' -``` - -### With a template (shortcut mode) - -Pass a template ID and the primitive fills in prompts, reference images, and style rules automatically. You can still override any parameter. - -```bash -# Same image, but the template provides the prompt and reference images -curl -X POST https://recoup-api.vercel.app/api/content/image \ - -H "x-api-key: YOUR_KEY" \ - -H "Content-Type: application/json" \ - -d '{"template": "artist-caption-bedroom", "reference_image_url": "YOUR_FACE_IMAGE"}' -``` - -Use [GET /api/content/templates](/api-reference/content/templates) to see available templates with descriptions. - -## Templates - -A template is a complete creative recipe — it defines what a piece of content looks like across every primitive: - -- **Image config**: prompt, reference images, style rules (camera, lighting, composition) -- **Video config**: mood variations, movement descriptions -- **Caption config**: tone, formatting rules, example captions -- **Edit config**: crop ratio, text overlay style, audio mixing - -Templates are optional. They save time by pre-filling parameters with curated defaults. When you see customers repeatedly creating the same kind of content, that pattern becomes a template. - -### Override priority - -When using a template, your explicit parameters always win: - -1. **Your params** — highest priority. What you pass overrides everything. -2. **Artist context** — if the artist has a style guide, it personalizes the template. -3. **Template defaults** — lowest priority. The recipe's built-in values. - -## Architecture - -``` -┌──────────────────────────────────────────────┐ -│ SKILL (advisory) │ -│ Teaches the agent which template to pick, │ -│ when to override, when to skip templates │ -├──────────────────────────────────────────────┤ -│ TEMPLATE (optional, enforced when used) │ -│ Complete creative recipe — image + video + │ -│ caption + edit config. Applied server-side. │ -├──────────────────────────────────────────────┤ -│ PRIMITIVES (execution) │ -│ image, video, caption, transcribe, edit, │ -│ upscale, analyze. Work with or without │ -│ templates. Do exactly what they're told. │ -└──────────────────────────────────────────────┘ -``` - -## Video Modes - -The video primitive supports 6 generation modes: - -| Mode | What it does | Required inputs | -|------|-------------|-----------------| -| `prompt` | Create from text description | `prompt` | -| `animate` | Animate a still image | `image_url`, `prompt` | -| `reference` | Use image as style reference (not first frame) | `image_url`, `prompt` | -| `extend` | Continue an existing video | `video_url`, `prompt` | -| `first-last` | Transition between two images | `image_url`, `end_image_url`, `prompt` | -| `lipsync` | Sync face to audio | `image_url`, `audio_url` | - -Set `mode` explicitly, or omit it and the API infers the mode from the inputs you provide. - -## Iteration - -Each primitive is independent. Redo any step without rerunning the whole pipeline: - -- Bad image? Regenerate with a different prompt or reference -- Caption too long? Regenerate with `length: "short"` -- Video glitchy? Analyze it, then regenerate with adjusted params -- Clip too short? Use `extend` mode to continue it -- Low quality? Upscale the image or video -- Everything good but wrong caption? Just re-run the edit step From 849d1df21d11845b63e10bc376520df95f57b1b7 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Thu, 9 Apr 2026 07:22:50 -0500 Subject: [PATCH 4/4] fix: address CodeRabbit review feedback on OpenAPI spec 1. ContentCreateResponse: remove template from required, allow null 2. ContentTemplateDetail: add full operation properties to edit schema 3. Remove duplicate PATCH /api/content/video (keep PATCH /api/content) 4. Add missing Music schemas (MusicComposeRequest, MusicComposeDetailedRequest, MusicStreamRequest, MusicCreatePlanRequest, MusicCreatePlanResponse, MusicErrorResponse) Co-Authored-By: Claude Opus 4.6 (1M context) --- api-reference/openapi.json | 288 ++++++++++++++++++++++++++++--------- 1 file changed, 219 insertions(+), 69 deletions(-) diff --git a/api-reference/openapi.json b/api-reference/openapi.json index bc8c9b5..08ba935 100644 --- a/api-reference/openapi.json +++ b/api-reference/openapi.json @@ -5911,70 +5911,6 @@ } } } - }, - "patch": { - "description": "Apply edits to a video or audio file — trim, crop, resize, overlay text, or add an audio track. Pass a `template` for a preset edit pipeline, or build your own with an `operations` array. Everything runs in one pass.", - "security": [ - { - "apiKeyAuth": [] - }, - { - "bearerAuth": [] - } - ], - "requestBody": { - "description": "Edit parameters", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ContentCreateEditRequest" - } - } - } - }, - "responses": { - "202": { - "description": "Edit task triggered successfully. Poll via [GET /api/tasks/runs](/api-reference/tasks/runs) using the returned `runId`.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ContentCreateEditResponse" - } - } - } - }, - "400": { - "description": "Validation failed — invalid or missing request body fields", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" - } - } - } - }, - "401": { - "description": "Unauthorized — invalid or missing API key", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ContentErrorResponse" - } - } - } - } - } } }, "/api/content/transcribe": { @@ -13555,8 +13491,7 @@ "required": [ "runIds", "status", - "artist_account_id", - "template" + "artist_account_id" ], "description": "Confirmation that the content creation pipeline has been triggered. Always returns `runIds` as an array — even for a single run, it contains one element.", "properties": { @@ -13584,9 +13519,12 @@ "example": "1873859c-dd37-4e9a-9bac-80d3558527a9" }, "template": { - "type": "string", - "description": "The template being used", - "example": "artist-caption-bedroom" + "type": [ + "string", + "null" + ], + "description": "Template ID when a preset pipeline is used; null in malleable mode.", + "example": null }, "lipsync": { "type": "boolean", @@ -13806,6 +13744,64 @@ "mux_audio" ], "description": "Operation type" + }, + "start": { + "type": "number", + "description": "Start time in seconds (trim)" + }, + "duration": { + "type": "number", + "description": "Duration in seconds (trim)" + }, + "aspect": { + "type": "string", + "description": "Aspect ratio e.g. 9:16 (crop)" + }, + "width": { + "type": "integer", + "description": "Width in pixels (crop/resize)" + }, + "height": { + "type": "integer", + "description": "Height in pixels (crop/resize)" + }, + "content": { + "type": "string", + "description": "Text content (overlay_text)" + }, + "font": { + "type": "string", + "description": "Font name (overlay_text)" + }, + "color": { + "type": "string", + "description": "Text color (overlay_text)" + }, + "stroke_color": { + "type": "string", + "description": "Text stroke color (overlay_text)" + }, + "max_font_size": { + "type": "number", + "description": "Maximum font size (overlay_text)" + }, + "position": { + "type": "string", + "enum": [ + "top", + "center", + "bottom" + ], + "description": "Text position (overlay_text)" + }, + "audio_url": { + "type": "string", + "format": "uri", + "description": "Audio URL to mux (mux_audio)" + }, + "replace": { + "type": "boolean", + "description": "Replace existing audio (mux_audio)" } } } @@ -16220,6 +16216,160 @@ "description": "Results formatted as markdown for easy reading." } } + }, + "MusicComposeRequest": { + "type": "object", + "description": "Parameters for generating a song. Provide either a text prompt or a composition_plan.", + "properties": { + "prompt": { + "type": "string", + "description": "Text prompt describing the desired song — mood, genre, instruments, lyrics, structure." + }, + "composition_plan": { + "type": "object", + "description": "Full composition plan for fine-grained control over sections, styles, and lyrics. Use POST /api/music/plan to generate one." + }, + "duration": { + "type": "number", + "description": "Desired duration in seconds.", + "default": 30 + }, + "output_format": { + "type": "string", + "description": "Audio output format (e.g. mp3_44100_192).", + "default": "mp3_44100_192" + }, + "sign_with_c2pa": { + "type": "boolean", + "description": "Sign the output with C2PA content credentials.", + "default": false + } + } + }, + "MusicComposeDetailedRequest": { + "type": "object", + "description": "Parameters for generating a song with metadata and optional word-level timestamps.", + "properties": { + "prompt": { + "type": "string", + "description": "Text prompt describing the desired song — mood, genre, instruments, lyrics, structure." + }, + "composition_plan": { + "type": "object", + "description": "Full composition plan for fine-grained control over sections, styles, and lyrics. Use POST /api/music/plan to generate one." + }, + "duration": { + "type": "number", + "description": "Desired duration in seconds.", + "default": 30 + }, + "output_format": { + "type": "string", + "description": "Audio output format (e.g. mp3_44100_192).", + "default": "mp3_44100_192" + }, + "sign_with_c2pa": { + "type": "boolean", + "description": "Sign the output with C2PA content credentials.", + "default": false + }, + "with_timestamps": { + "type": "boolean", + "description": "Include word-level timestamps in the metadata response.", + "default": false + } + } + }, + "MusicStreamRequest": { + "type": "object", + "description": "Parameters for generating a song with real-time audio streaming.", + "properties": { + "prompt": { + "type": "string", + "description": "Text prompt describing the desired song — mood, genre, instruments, lyrics, structure." + }, + "composition_plan": { + "type": "object", + "description": "Full composition plan for fine-grained control over sections, styles, and lyrics. Use POST /api/music/plan to generate one." + }, + "duration": { + "type": "number", + "description": "Desired duration in seconds.", + "default": 30 + }, + "output_format": { + "type": "string", + "description": "Audio output format (e.g. mp3_44100_192).", + "default": "mp3_44100_192" + }, + "sign_with_c2pa": { + "type": "boolean", + "description": "Sign the output with C2PA content credentials.", + "default": false + } + } + }, + "MusicCreatePlanRequest": { + "type": "object", + "required": [ + "prompt" + ], + "description": "Parameters for creating a composition plan from a text prompt.", + "properties": { + "prompt": { + "type": "string", + "description": "Text prompt describing the desired song." + } + } + }, + "MusicCreatePlanResponse": { + "type": "object", + "description": "A structured composition plan that can be reviewed, edited, and passed to the compose endpoint.", + "properties": { + "plan": { + "type": "object", + "description": "The composition plan with sections, styles, lyrics, and durations.", + "properties": { + "sections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Section name (e.g. intro, verse, chorus)" + }, + "duration": { + "type": "number", + "description": "Section duration in seconds" + }, + "style": { + "type": "string", + "description": "Style description for this section" + }, + "lyrics": { + "type": "string", + "description": "Lyrics for this section" + } + } + } + } + } + } + } + }, + "MusicErrorResponse": { + "type": "object", + "required": [ + "error" + ], + "description": "Error response from a music endpoint.", + "properties": { + "error": { + "type": "string", + "description": "Error message describing what went wrong." + } + } } }, "securitySchemes": {