GhostfeedGhostfeed MCPv6.0.0
MCP docs

UGC Reactions

Create UGC reaction videos over the two-phase flow: import or crop a template, reuse, render, or edit an avatar first frame, then, after approval, animate it by cloning the reference motion or following a text prompt. Deletion stays in the dashboard.

list_reaction_templates

READ

REST: GET /api/v2/reaction-templates

FIRST source search for every new reaction request unless the user explicitly supplied an exact source. List reaction TEMPLATES usable in this workspace: platform stock plus your own imported/uploaded motion clips. Each has an id, name, category, duration, thumbnail, and status. Captioned clips also carry opensOn (the opening still your avatar gets rendered into) and motion (what happens in the clip); older clips may not have them yet. Choose primarily on opensOn. A prompt-mode video may reuse the opening pose without copying the source motion. Use motion only to judge a requested clone, and never paste it into generate_reaction_video as the prompt. If no template has a reasonable opening-pose match, search list_inspiration_reactions next.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    },
    "mine": {
      "type": "boolean"
    },
    "includeUnusable": {
      "description": "Include templates that cannot be generated from yet: ones still importing, ones that failed, and ones over 30s awaiting a crop. Off by default, because generating from them is refused anyway.",
      "type": "boolean"
    },
    "page": {
      "description": "Page number (default 1).",
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "limit": {
      "description": "Per page (max 25; lower than other tools because these rows carry long descriptions). Each tool documents its own default.",
      "type": "integer",
      "minimum": 1,
      "maximum": 25
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "templates": [
      {
        "id": "user-1a2b3c4d-1737100000000",
        "name": "Excited point",
        "category": "Reactions",
        "durationSeconds": 4.2,
        "thumbnailUrl": "https://cdn.ghostfeed.ai/reactions/user-1a2b3c4d/thumbnail.jpg",
        "kind": "owned",
        "status": "completed",
        "mention": "@template:Excited point (user-1a2b3c4d-1737100000000)",
        "opensOn": "Medium shot of a woman at a desk, looking just off camera, hands raised mid-gesture.",
        "motion": "She points at the camera, leans in, and breaks into a grin."
      }
    ],
    "total": 1,
    "page": 1,
    "pages": 1,
    "nextSteps": "Pick a template, then generate_reaction_frames with its id and one or more avatars."
  }
}

get_reaction_template

READ

REST: GET /api/v2/reaction-templates/:id

Read one source template by its templateId (or a generated video’s sourceReactionId). Returns the original/source clip, external import URL when present, duration, import type, the full opensOn still description, and the full stored motion analysis. Every clip is analysed automatically on import, so this free read is normally where the complete motionAnalysis.prompt already is. list_reaction_templates only carries a one-line teaser of it. Use it after list_reaction_videos to resolve each unique source once, rather than repeating source metadata on every generated video.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    },
    "templateId": {
      "type": "string",
      "minLength": 1,
      "description": "The template id from list_reaction_templates or sourceReactionId from a video."
    }
  },
  "required": [
    "templateId"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "template": {
      "templateId": "reaction-7ab2",
      "name": "Excited point source",
      "category": "reaction",
      "kind": "owned",
      "status": "completed",
      "opensOn": "Waist-up, phone held at arm’s length in a bright kitchen. She looks straight down the lens, both hands empty, warm window light from the left.",
      "source": {
        "videoUrl": "https://cdn.ghostfeed.ai/reactions/excited-point.mp4",
        "thumbnailUrl": "https://cdn.ghostfeed.ai/reactions/excited-point-first-frame.jpg",
        "durationSeconds": 7,
        "aspectRatio": "9:16",
        "importSource": "tiktok",
        "originalUrl": "https://www.tiktok.com/@creator/video/123"
      },
      "motionAnalysis": {
        "status": "complete",
        "prompt": "Timed section\\n0-7s: She points, then smiles.\\n\\nWhole-video description\\nAn upbeat point and smile.",
        "timeline": "0-7s: She points, then smiles.",
        "model": "gemini-3.1-flash-lite",
        "generatedAt": "2026-07-20T02:00:00.000Z"
      }
    }
  }
}

list_reaction_frames

READ

REST: GET /api/v2/reaction-frames

List reusable reaction FRAMES on the workspace board, newest first, including ones a person made in the dashboard. Filter by both avatar and template before generating a new first frame. Each row carries its image, lineage and generation metadata, preferenceStatus, videoStage, and animationStatus. A complete frame is still reusable for another video. Show candidates and let the user reuse one for free or request a fresh variation; never silently assume an old take is approved. To SHOW these results in an MCP Apps host, ALWAYS call render_image_results; never hand-build the presentation in chat (no markdown galleries, embedded thumbnails, numbered image lists, or artifacts), even after a widget error. Outside an Apps host, describe items in text and share the dashboard link.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    },
    "avatar": {
      "type": "string",
      "minLength": 1
    },
    "templateId": {
      "type": "string",
      "minLength": 1
    },
    "page": {
      "description": "Page number (default 1).",
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "limit": {
      "description": "Per page (max 100). Each tool documents its own default.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "frames": [
      {
        "frameId": "4b706a83-c035-427d-9f68-1091101c6dec",
        "imageUrl": "https://cdn.ghostfeed.ai/frames/frame-1737100000000-1.png",
        "avatarId": "d7d06ea3-a574-4fe0-9a39-d189a0b68698",
        "avatarName": "Ashley Gram",
        "templateId": "user-1a2b3c4d-1737100000000",
        "videoId": "1cc27551-5ad9-42bd-9a9c-899f83dcfec6",
        "imageModel": "gemini_flash",
        "imageOutputResolution": "standard",
        "videoStage": "selecting-frame",
        "preferenceStatus": "neutral",
        "animationStatus": "never",
        "isAnimated": false,
        "createdAt": "2026-07-20T02:22:50.374Z",
        "mention": "@frame:Ashley Gram (4b706a83-c035-427d-9f68-1091101c6dec)",
        "dashboardUrl": "https://app.ghostfeed.ai/w/client-a/ugc-reactions?frame=4b706a83-c035-427d-9f68-1091101c6dec"
      }
    ],
    "total": 1,
    "page": 1,
    "pages": 1,
    "nextSteps": "Pass a frameId to generate_reaction_video to animate it."
  }
}

list_reaction_videos

READ

REST: GET /api/v2/reaction-videos

List the reaction VIDEOS in this workspace, newest first, agent-made or not. Each has a videoId, name, stage, a playable videoUrl once stage is "complete", a dashboardUrl, and sourceReactionId when it came from a template. videoUrl is the canonical clip: the post-edit version once one exists, otherwise the raw render. generation is a compact model/mode/duration summary. Rows carry avatarId but not the avatar name; resolve names with list_avatars when you need them. Call get_reaction_template with sourceReactionId for source media and Gemini analysis, or get_reaction_video for the exact prompt/settings of one rendered video. Managing and deleting videos stays in the dashboard. To SHOW these results in an MCP Apps host, ALWAYS call render_video_result; never hand-build the presentation in chat (no markdown galleries, embedded thumbnails, numbered image lists, or artifacts), even after a widget error. Outside an Apps host, describe items in text and share the dashboard link.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    },
    "avatar": {
      "type": "string",
      "minLength": 1
    },
    "stage": {
      "type": "string",
      "minLength": 1
    },
    "page": {
      "description": "Page number (default 1).",
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "limit": {
      "description": "Per page (max 100). Each tool documents its own default.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "videos": [
      {
        "videoId": "b9b2a07c-2119-455f-aebf-e7d0b2f32182",
        "name": "Excited point - Ashley Gram",
        "stage": "complete",
        "videoUrl": "https://cdn.ghostfeed.ai/generated-videos/b9b2a07c/final.mp4",
        "sourceReactionId": "reaction-7ab2",
        "generation": {
          "firstFrame": {
            "mode": "template",
            "imageModel": "gemini_flash"
          },
          "video": {
            "mode": "grok",
            "durationSeconds": 7
          }
        },
        "avatarId": "d7d06ea3-a574-4fe0-9a39-d189a0b68698",
        "createdAt": "2026-07-20T02:09:51.871Z",
        "dashboardUrl": "https://app.ghostfeed.ai/w/client-a/ugc-reactions?video=b9b2a07c-2119-455f-aebf-e7d0b2f32182"
      }
    ],
    "total": 1,
    "page": 1,
    "pages": 1,
    "nextSteps": "Use videoUrl to inspect generated clips. Call get_reaction_template with sourceReactionId for the original source, or get_reaction_video for exact prompts and settings. Rows carry avatarId but not the avatar name; resolve names with list_avatars."
  }
}

get_reaction_video

READ

REST: GET /api/v2/reaction-videos/:id

Read one reaction video’s full render record after finding it with list_reaction_videos. Returns the canonical generated video URL, sourceReactionId, exact first-frame and video prompts, selected frame, models, duration, and post-edit URL. Call get_reaction_template separately for source media and Gemini analysis. Use this only for videos you need to recreate or inspect in detail; the list stays compact enough to inventory up to 100 videos. To SHOW these results in an MCP Apps host, ALWAYS call render_video_result; never hand-build the presentation in chat (no markdown galleries, embedded thumbnails, numbered image lists, or artifacts), even after a widget error. Outside an Apps host, describe items in text and share the dashboard link.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    },
    "videoId": {
      "type": "string",
      "minLength": 1,
      "description": "The videoId from list_reaction_videos whose complete render metadata to read."
    }
  },
  "required": [
    "videoId"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "video": {
      "videoId": "b9b2a07c-2119-455f-aebf-e7d0b2f32182",
      "name": "Excited point - Ashley Gram",
      "stage": "complete",
      "videoUrl": "https://cdn.ghostfeed.ai/generated-videos/b9b2a07c/final.mp4",
      "sourceReactionId": "reaction-7ab2",
      "generation": {
        "firstFrame": {
          "mode": "template",
          "imageModel": "gemini_flash",
          "prompt": "Render Mila in the source opening pose.",
          "stylingPrompt": "Keep the white blouse and bright kitchen.",
          "referenceImageUrl": "https://cdn.ghostfeed.ai/references/mila.jpg",
          "sourceFrameId": "frame-1737099999999-1",
          "aspectRatio": "9:16",
          "selectedFrameUrl": "https://cdn.ghostfeed.ai/frames/frame-1737100000000-1.png"
        },
        "video": {
          "mode": "grok",
          "prompt": "She points excitedly at the camera, then smiles.",
          "durationSeconds": 7,
          "finalVideoUrl": "https://cdn.ghostfeed.ai/generated-videos/b9b2a07c/final.mp4"
        }
      },
      "avatarId": "d7d06ea3-a574-4fe0-9a39-d189a0b68698",
      "avatarName": "Ashley Gram",
      "createdAt": "2026-07-20T02:09:51.871Z",
      "dashboardUrl": "https://app.ghostfeed.ai/w/client-a/ugc-reactions?video=b9b2a07c-2119-455f-aebf-e7d0b2f32182"
    },
    "nextSteps": "Call get_reaction_template with sourceReactionId for the original source. Reuse recorded settings only after approval."
  }
}

generate_reaction_prompt

WRITE

REST: POST /api/v2/reaction-prompts

Generate the exact prompt the user will review before video creation. promptProfile prompt_based returns the normal visual-action prompt and remains a repair path when the template motionAnalysis is missing. For MiniMax H3, audio_guided and video_guided are on-demand profiles and require outputDurationSeconds. Their returned prompt visibly includes a Mandatory reference guidance section followed by Action guidance. Show the complete returned prompt, H3 plan, settings, warning, and estimated credits to the user. Tell the user not to change Mandatory reference guidance and to make only small changes inside Action guidance. Every profile still uses the selected first frame during video generation.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "templateId": {
      "type": "string",
      "minLength": 1
    },
    "promptProfile": {
      "type": "string",
      "enum": [
        "prompt_based",
        "audio_guided",
        "video_guided"
      ]
    },
    "outputDurationSeconds": {
      "type": "integer",
      "minimum": 4,
      "maximum": 15
    },
    "outputResolution": {
      "type": "string",
      "enum": [
        "768p",
        "2k"
      ]
    }
  },
  "required": [
    "workspace",
    "templateId"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "templateId": "user-1a2b3c4d-1737100000000",
    "prompt": "Mandatory reference guidance - do not edit\nUse the supplied reference video as the authoritative guide for motion and editing. When original reference audio is supplied, use it as the only audio. When no reference audio is supplied, keep the generated clip silent.\n\nAction guidance - small edits only\n\nSource clip plan\nClip 1 — 0-2.5s: Lean forward with a surprised smile.\nHARD CUT at 2.5s. Do not morph or interpolate.\nClip 2 — 2.5-7s: Point once, then settle into a warm expression.",
    "timeline": "0.0-2.5s: She leans toward the camera with a surprised smile.\n2.5-7.0s: She points once, then settles into a warm expression.",
    "model": "gemini-3.1-flash-lite",
    "cached": false,
    "promptProfile": "video_guided",
    "sourceDurationSeconds": 7,
    "minimaxH3Plan": {
      "mode": "video_guided",
      "outputDurationSeconds": 7,
      "outputResolution": "768p",
      "estimatedCredits": 14,
      "lineItems": [
        {
          "kind": "output_seconds",
          "seconds": 7,
          "creditsPerSecond": 1,
          "credits": 7
        },
        {
          "kind": "reference_video_seconds",
          "seconds": 7,
          "creditsPerSecond": 1,
          "credits": 7
        }
      ],
      "warning": "The complete provider prompt is shown above. Do not change the Mandatory reference guidance section. Make only small changes inside Action guidance because larger changes can reduce transfer accuracy."
    }
  }
}

list_inspiration_reactions

READ

REST: GET /api/v2/inspiration-reactions

SECOND source search for every new reaction request, after list_reaction_templates has no suitable opening pose. Browse the curated inspiration library of reaction clips (ids, names, categories, thumbnails). Captioned clips also carry opensOn (the opening still your avatar gets rendered into) and motion (what happens in the clip). render_inspiration_browser (workspace plus optional category) loads 12 real library items per page and changes pages without another model turn. A selection asks what the user wants to create and does not start paid work. Choose primarily on opensOn. A prompt-mode video may reuse the opening pose without copying the source motion. Use motion only to judge a requested clone, and never paste it into generate_reaction_video as the prompt. Pass an inspiration id to import_reaction_template or generate_reaction_frames and it is auto-saved to this workspace as a template first. Only fall back to a custom referenceImageUrl after both libraries lack a reasonable match. To SHOW these results in an MCP Apps host, ALWAYS call render_inspiration_browser; never hand-build the presentation in chat (no markdown galleries, embedded thumbnails, numbered image lists, or artifacts), even after a widget error. Outside an Apps host, describe items in text and share the dashboard link.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "description": "Target workspace: a slug, id, or exact name from list_workspaces. Omit to use this connection's default workspace. Must be inside the credential's grant and your current access; overrides the connection-level X-Workspace-Id header.",
      "type": "string"
    },
    "category": {
      "type": "string",
      "maxLength": 80
    },
    "page": {
      "description": "Page number (default 1).",
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "limit": {
      "description": "Per page (max 100). Each tool documents its own default.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "inspiration": [
      {
        "id": "insp_9f8e7d6c",
        "name": "Grandma tastes pickle",
        "category": "Food",
        "durationSeconds": 6.1,
        "videoUrl": "https://cdn.ghostfeed.ai/inspiration/insp_9f8e7d6c/video.mp4",
        "thumbnailUrl": "https://cdn.ghostfeed.ai/inspiration/insp_9f8e7d6c/thumb.jpg",
        "mention": "@inspiration:Grandma tastes pickle (insp_9f8e7d6c)"
      }
    ],
    "total": 1,
    "page": 1,
    "pages": 1,
    "nextSteps": "Pass an inspiration id to generate_reaction_frames; it is auto-saved as a template."
  }
}

list_reaction_video_modes

READ

REST: GET /api/v2/reaction-video-modes

List the video modes for the reaction video phase, with costs, resolutions, prompt rules, and audio behavior. MiniMax H3 also returns minimaxH3Modes: prompt_based, audio_guided, and video_guided. Every H3 mode uses the selected frame. Audio-guided adds free source audio. Video-guided adds billed source-video input and includes its audio when present. Read these fields before creating an H3 prompt or video.

No input. Call with no arguments.

Example response
{
  "success": true,
  "data": {
    "modes": [
      {
        "mode": "one_to_one_standard",
        "label": "1:1 Standard",
        "family": "clone",
        "creditsPerSecond": 1,
        "supportedResolutions": [
          "720p",
          "1080p"
        ],
        "defaultResolution": "720p",
        "supportsAudio": false,
        "audioEnabledByDefault": false,
        "needsPrompt": false,
        "isDefault": true
      },
      {
        "mode": "seedance_2_0_fast",
        "label": "Seedance 2.0 Fast",
        "family": "prompt",
        "creditsPerSecond": 1.5,
        "supportedResolutions": [
          "720p"
        ],
        "defaultResolution": "720p",
        "supportsAudio": true,
        "audioEnabledByDefault": true,
        "needsPrompt": true,
        "maxPromptCharacters": 2000,
        "durationOptions": [
          4,
          5,
          6,
          7,
          8,
          9,
          10,
          11,
          12,
          13,
          14,
          15
        ],
        "supportsAutoDuration": true,
        "isDefault": true
      },
      {
        "mode": "minimax_h3",
        "label": "MiniMax H3",
        "family": "prompt",
        "creditsPerSecond": 1,
        "supportedResolutions": [
          "768p",
          "2k"
        ],
        "defaultResolution": "768p",
        "supportsAudio": false,
        "audioEnabledByDefault": false,
        "needsPrompt": true,
        "maxPromptCharacters": 7000,
        "durationOptions": [
          4,
          5,
          6,
          7,
          8,
          9,
          10,
          11,
          12,
          13,
          14,
          15
        ],
        "supportsAutoDuration": false,
        "minimaxH3Modes": [
          {
            "mode": "prompt_based",
            "label": "Prompt based",
            "requiredInputs": [
              "selected_frame",
              "prompt"
            ],
            "inputCreditsPerSecondByResolution": {
              "768p": 0,
              "2k": 0
            }
          },
          {
            "mode": "audio_guided",
            "label": "Audio guided",
            "requiredInputs": [
              "selected_frame",
              "source_audio",
              "prompt"
            ],
            "inputCreditsPerSecondByResolution": {
              "768p": 0,
              "2k": 0
            }
          },
          {
            "mode": "video_guided",
            "label": "Video guided",
            "requiredInputs": [
              "selected_frame",
              "source_video",
              "prompt"
            ],
            "inputCreditsPerSecondByResolution": {
              "768p": 1,
              "2k": 1
            }
          }
        ],
        "defaultMiniMaxH3Mode": "prompt_based",
        "isDefault": false
      }
    ],
    "defaults": {
      "clone": "one_to_one_standard",
      "prompt": "seedance_2_0_fast"
    },
    "nextSteps": "Clone modes reuse a template motion; prompt modes animate from text. Check each mode’s supportedResolutions before selecting a resolution. MiniMax H3 defaults to 768p and has prompt_based, audio_guided, and video_guided modes. Every H3 mode uses the selected frame. Modes with audioEnabledByDefault true generate audio unless enableAudio is false."
  }
}

request_reaction_upload

WRITEIDEMPOTENT

REST: POST /api/v2/reactions/upload-url

Get a short-lived presigned R2 upload URL for a LOCAL video file (a video is too large for a tool argument). PUT the raw bytes to uploadUrl with the given Content-Type, then pass the returned fileUrl to import_reaction_template as uploadedFileUrl. Costs no credits.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "filename": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "contentType": {
      "type": "string",
      "minLength": 3,
      "maxLength": 100
    }
  },
  "required": [
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "upload": {
      "method": "PUT",
      "uploadUrl": "https://abc123.r2.cloudflarestorage.com/shortsbro/users/1a2b3c4d/reactions-uploads/2026/07/18/uuid.mp4?X-Amz-Signature=...",
      "fileUrl": "https://cdn.ghostfeed.ai/users/1a2b3c4d/reactions-uploads/2026/07/18/uuid.mp4",
      "contentType": "video/mp4",
      "expiresInSeconds": 900
    },
    "nextSteps": "PUT the bytes to uploadUrl, then import_reaction_template with uploadedFileUrl=fileUrl."
  }
}

crop_reaction_template

WRITEIDEMPOTENT

REST: POST /api/v2/reaction-templates/crop

Create one reusable reaction template from an exact timestamp range of an existing owned/imported template. Async and free: poll the returned reaction_crop generation. On success output.id is the new template id. Use when the user supplies or approves exact start/end times; this creates a derivative and archives the source only when the source was blocked in needs_crop.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "templateId": {
      "type": "string",
      "minLength": 1
    },
    "startSeconds": {
      "type": "number",
      "minimum": 0
    },
    "endSeconds": {
      "type": "number",
      "exclusiveMinimum": 0
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 180
    },
    "category": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    },
    "idempotencyKey": {
      "description": "A stable key to reuse if you retry this exact request; a repeat with the same key is NOT charged again.",
      "type": "string",
      "maxLength": 200
    }
  },
  "required": [
    "workspace",
    "templateId",
    "startSeconds",
    "endSeconds"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "generation": {
      "id": "gen_crop_aa11bb22",
      "kind": "reaction_crop",
      "state": "running",
      "phase": "cropping",
      "input": {
        "templateId": "user-1a2b3c4d-1737100000000",
        "startSeconds": 2,
        "endSeconds": 9
      },
      "outputs": [],
      "dashboardUrl": "https://ghostfeed.ai/w/client-a/ugc-reactions?template=user-1a2b3c4d-1737100000000&ref=agent",
      "createdAt": "2026-07-28T12:00:00.000Z",
      "updatedAt": "2026-07-28T12:00:00.000Z"
    },
    "nextSteps": "Poll get_generation; output.id is the new cropped template id."
  }
}

smart_crop_reaction_template

WRITEIDEMPOTENT

REST: POST /api/v2/reaction-templates/smart-crop

Split an existing owned/imported reaction source into up to 8 reusable templates using FFmpeg scene-change detection. This is cut detection, not semantic highlight selection. Async and free: poll the returned reaction_smart_crop generation; outputs appear incrementally and each output.id is a new template id.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "templateId": {
      "type": "string",
      "minLength": 1
    },
    "maxClips": {
      "type": "integer",
      "minimum": 1,
      "maximum": 8
    },
    "namePrefix": {
      "type": "string",
      "minLength": 1,
      "maxLength": 180
    },
    "category": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    },
    "idempotencyKey": {
      "description": "A stable key to reuse if you retry this exact request; a repeat with the same key is NOT charged again.",
      "type": "string",
      "maxLength": 200
    }
  },
  "required": [
    "workspace",
    "templateId"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "generation": {
      "id": "gen_smart_crop_cc33dd44",
      "kind": "reaction_smart_crop",
      "state": "running",
      "phase": "cropping",
      "input": {
        "templateId": "user-1a2b3c4d-1737100000000",
        "maxClips": 6
      },
      "outputs": [],
      "dashboardUrl": "https://ghostfeed.ai/w/client-a/ugc-reactions?template=user-1a2b3c4d-1737100000000&ref=agent",
      "createdAt": "2026-07-28T12:00:00.000Z",
      "updatedAt": "2026-07-28T12:00:00.000Z"
    },
    "nextSteps": "Poll get_generation; outputs appear incrementally and each output.id is a cropped template."
  }
}

import_reaction_template

WRITE

REST: POST /api/v2/reaction-templates/import

Import a reaction TEMPLATE from exactly one source: sourceUrl (a TikTok/Instagram link), uploadedFileUrl (from request_reaction_upload, the local-file path), or inspirationId (a curated clip). Async: returns a reaction_import generation to poll with get_generation. succeeded = ready to use; needs_action = the source is 30-120s and must be cropped in the dashboard first (open dashboardUrl); >120s is rejected. Costs no credits. Managing, cropping, and deleting templates all happen in the dashboard.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120
    },
    "category": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    },
    "sourceUrl": {
      "type": "string",
      "format": "uri"
    },
    "uploadedFileUrl": {
      "type": "string",
      "format": "uri"
    },
    "inspirationId": {
      "type": "string",
      "minLength": 1
    },
    "idempotencyKey": {
      "description": "A stable key to reuse if you retry this exact request; a repeat with the same key is NOT charged again.",
      "type": "string",
      "maxLength": 200
    }
  },
  "required": [
    "workspace",
    "name"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "generation": {
      "id": "gen_5b6c7d8e9f0a",
      "kind": "reaction_import",
      "state": "running",
      "title": "Excited point",
      "input": {
        "templateId": "user-1a2b3c4d-1737100000000",
        "source": "link"
      },
      "outputs": [],
      "dashboardUrl": "https://ghostfeed.ai/w/client-a/ugc-reactions?template=user-1a2b3c4d-1737100000000&ref=agent",
      "createdAt": "2026-07-18T12:00:00.000Z",
      "updatedAt": "2026-07-18T12:00:00.000Z"
    },
    "templateId": "user-1a2b3c4d-1737100000000",
    "mention": "@template:Excited point (user-1a2b3c4d-1737100000000)",
    "nextSteps": "Poll get_generation until terminal. needs_action = crop in the dashboard first."
  }
}

generate_reaction_frames

WRITE

REST: POST /api/v2/reactions/frames

PHASE 1 of a UGC reaction: create ONE first frame per avatar. With a source, render the avatar into the source pose. Without a source, supply a non-empty stylingPrompt to create the frame directly from the avatar. Source is at most one of templateId, inspirationId (auto-saved to a template first), or referenceImageUrl. For a source-based request, search list_reaction_templates first, then list_inspiration_reactions if the user did not supply an exact source. imageOutputResolution defaults to standard. Source-based work is async and must be polled. Prompt-only work normally returns succeeded reaction_frame generations; a concurrent idempotent replay can return the same queued generation and must be polled. In both flows, the finished frame is output.url and output.id is the frameId. For async generations, immediately call render_image_results with one generating frame item per generation: set generationId to the returned generation id and keep the shared prompt, image model, and requested aspect ratio. The widget polls the read-only result itself. The selected image model and resolution charge applies per avatar. Show all frames and get approval before generate_reaction_video.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "avatars": {
      "minItems": 1,
      "maxItems": 10,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "templateId": {
      "type": "string",
      "minLength": 1
    },
    "inspirationId": {
      "type": "string",
      "minLength": 1
    },
    "referenceImageUrl": {
      "type": "string",
      "format": "uri"
    },
    "imageModel": {
      "type": "string",
      "enum": [
        "gemini_flash_lite",
        "gemini_flash",
        "gemini_pro",
        "gpt_low",
        "gpt_medium",
        "gpt_high"
      ]
    },
    "imageOutputResolution": {
      "type": "string",
      "enum": [
        "standard",
        "1080p"
      ]
    },
    "stylingPrompt": {
      "type": "string",
      "maxLength": 500
    },
    "aspectRatio": {
      "type": "string",
      "enum": [
        "9:16",
        "16:9",
        "1:1"
      ]
    },
    "idempotencyKey": {
      "description": "A stable key to reuse if you retry this exact request; a repeat with the same key is NOT charged again.",
      "type": "string",
      "maxLength": 200
    }
  },
  "required": [
    "workspace",
    "avatars"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "generations": [
      {
        "id": "gen_frame_aa11bb22",
        "kind": "reaction_frame",
        "state": "running",
        "phase": "generating-frames",
        "input": {
          "videoId": "vid_aa11bb22",
          "source": "template",
          "imageModel": "gemini_flash"
        },
        "outputs": [],
        "dashboardUrl": "https://ghostfeed.ai/w/client-a/ugc-reactions?ref=agent",
        "createdAt": "2026-07-18T12:01:00.000Z",
        "updatedAt": "2026-07-18T12:01:00.000Z"
      }
    ],
    "estimatedCreditCost": 1,
    "creditsRemaining": 46,
    "nextSteps": "Poll each generation; the frame image is output.url. Get the user to approve before generate_reaction_video."
  }
}

edit_reaction_frame

WRITE

REST: POST /api/v2/reactions/frames/edit

Edit one existing reaction frame with a text prompt. Use frameId from list_reaction_frames. This creates a new frame and preserves the source frame plus its lineage. It charges the selected image-model and resolution cost once. The returned reaction_frame generation is normally succeeded; a concurrent idempotent replay can return the same queued generation and must be polled. On success, output.url is the edited image and output.id is the new frameId. Show the edit to the user and get approval before generate_reaction_video.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "frameId": {
      "type": "string",
      "minLength": 1,
      "description": "The frameId from list_reaction_frames to edit. The source frame stays unchanged."
    },
    "prompt": {
      "type": "string",
      "minLength": 1,
      "maxLength": 1500,
      "description": "Describe the change to make while preserving the source frame identity and composition."
    },
    "imageModel": {
      "type": "string",
      "enum": [
        "gemini_flash_lite",
        "gemini_flash",
        "gemini_pro",
        "gpt_low",
        "gpt_medium",
        "gpt_high"
      ]
    },
    "imageOutputResolution": {
      "type": "string",
      "enum": [
        "standard",
        "1080p"
      ]
    },
    "idempotencyKey": {
      "description": "A stable key to reuse if you retry this exact request; a repeat with the same key is NOT charged again.",
      "type": "string",
      "maxLength": 200
    }
  },
  "required": [
    "workspace",
    "frameId",
    "prompt"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "generation": {
      "id": "gen_frame_edit_bb22cc33",
      "kind": "reaction_frame",
      "state": "succeeded",
      "input": {
        "sourceFrameId": "frm_ee55ff66",
        "prompt": "Replace the shirt with a blue jacket.",
        "imageModel": "gemini_flash",
        "imageOutputResolution": "standard",
        "workspaceSlug": "client-a"
      },
      "outputs": [
        {
          "type": "image",
          "url": "https://cdn.ghostfeed.ai/reactions/frame-edit-bb22cc33.jpg",
          "id": "frm_edit_bb22cc33"
        }
      ],
      "creditsSpent": 1,
      "dashboardUrl": "https://ghostfeed.ai/w/client-a/ugc-reactions?ref=agent",
      "createdAt": "2026-08-08T12:01:00.000Z",
      "updatedAt": "2026-08-08T12:01:02.000Z"
    },
    "creditsRemaining": 45,
    "nextSteps": "Show the edited frame to the user and get approval before generate_reaction_video."
  }
}

generate_reaction_video

WRITE

REST: POST /api/v2/reactions/videos

PHASE 2 of a UGC reaction: animate approved frame(s) into video. Only call after the user has reviewed and approved the frames and exact prompt. For MiniMax H3, set minimaxH3Mode explicitly: prompt_based sends the selected frame and text; audio_guided also sends source audio; video_guided also sends the source video and includes its audio when present. Guided H3 calls require sourceTemplateId and durationSeconds, and sourceTemplateId must match every frame lineage. First call generate_reaction_prompt with the matching profile and show its complete prompt. The prompt already includes visible Mandatory reference guidance; do not remove or rewrite that section. Small user-approved changes belong only inside Action guidance. Ghostfeed sends the approved prompt without adding hidden text. Then call here with promptApproved true and a protective maxCredits. Other clone modes take no prompt and follow source duration. Other prompt modes use text and may support enableAudio. H3 defaults to 768p and supports optional 2k. Async: returns one reaction_video generation per frame to poll. Immediately call render_video_result with status generating, the generationId, prompt, model, method, aspect ratio, and relevant frame/avatar reference images. The view polls and replaces the pending card with the playable video.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "frameIds": {
      "minItems": 1,
      "maxItems": 8,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "mode": {
      "type": "string",
      "enum": [
        "one_to_one_standard",
        "one_to_one_clone_premium",
        "grok",
        "pixverse",
        "pixverse_1080p",
        "kling_2_5_turbo",
        "seedance_2_0",
        "seedance_2_0_fast",
        "seedance_2_0_mini",
        "minimax_h3"
      ]
    },
    "minimaxH3Mode": {
      "description": "MiniMax H3 input mode. Every value uses the selected frame. prompt_based adds text only; audio_guided also sends source audio; video_guided also sends the source video and includes its audio when present.",
      "type": "string",
      "enum": [
        "prompt_based",
        "audio_guided",
        "video_guided"
      ]
    },
    "sourceTemplateId": {
      "description": "Required for MiniMax H3 audio_guided and video_guided. It must match the source-template lineage of every selected frame.",
      "type": "string",
      "minLength": 1
    },
    "prompt": {
      "type": "string",
      "description": "Motion prompt for prompt modes. Call list_reaction_video_modes and obey the selected mode’s maxPromptCharacters; clone modes reject prompt."
    },
    "promptApproved": {
      "description": "Required as true for prompt modes. Set it only after showing the user the exact prompt (after trimming surrounding whitespace) and receiving explicit approval. Clone modes do not use a prompt and do not require this.",
      "type": "boolean"
    },
    "durationSeconds": {
      "type": "number",
      "exclusiveMinimum": 0
    },
    "enableAudio": {
      "description": "Controls synchronized generated audio for modes that advertise supportsAudio true. Unsupported modes reject true; MiniMax H3 output audio is provider-native and is not configurable here.",
      "type": "boolean"
    },
    "outputResolution": {
      "description": "Requested output resolution. Call list_reaction_video_modes first and use the selected mode’s supportedResolutions. Resolution availability and defaults are mode-specific; MiniMax H3 defaults to 768p and supports an optional 2k upgrade.",
      "type": "string",
      "enum": [
        "720p",
        "768p",
        "1080p",
        "2k"
      ]
    },
    "maxCredits": {
      "description": "Refuse to start any clip that would cost more than this many credits. Clone modes use the source clip length and the selected model/resolution rate. A per-clip ceiling of MAX_REACTION_VIDEO_CREDITS always applies; this only tightens it.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "idempotencyKey": {
      "description": "A stable key to reuse if you retry this exact request; a repeat with the same key is NOT charged again.",
      "type": "string",
      "maxLength": 200
    }
  },
  "required": [
    "workspace",
    "frameIds"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "generations": [
      {
        "id": "gen_vid_cc33dd44",
        "kind": "reaction_video",
        "state": "running",
        "phase": "generating-video",
        "input": {
          "videoId": "vid_aa11bb22",
          "frameId": "frm_ee55ff66",
          "mode": "minimax_h3",
          "minimaxH3Mode": "video_guided",
          "sourceTemplateId": "user-1a2b3c4d-1737100000000"
        },
        "outputs": [],
        "dashboardUrl": "https://ghostfeed.ai/w/client-a/ugc-reactions?video=vid_aa11bb22&ref=agent",
        "createdAt": "2026-07-18T12:05:00.000Z",
        "updatedAt": "2026-07-18T12:05:00.000Z"
      }
    ],
    "skipped": [],
    "creditsSpent": 8,
    "creditsRemaining": 38,
    "nextSteps": "Poll each generation until its state is succeeded; the clip is output.url. Report spend to the user."
  }
}

regenerate_reaction_frame

WRITE

REST: POST /api/v2/reactions/frames/regenerate

Redo a frame the user was not happy with. Identify it with EITHER frameId (any frame from list_reaction_frames, including ones a person made in the dashboard) OR generationId (a frame this API started); pass exactly one. Re-runs frame generation on the same avatar + source and charges another frame credit. Poll the same generation until it succeeds with a fresh frame, then get approval before generate_reaction_video.

Input schema
{
  "type": "object",
  "properties": {
    "workspace": {
      "type": "string",
      "minLength": 1,
      "description": "Required write target: a workspace slug or id from list_workspaces. Writes never inherit the connection default."
    },
    "generationId": {
      "description": "The generationId of a frame you started through the agent API.",
      "type": "string",
      "minLength": 1
    },
    "frameId": {
      "description": "A frameId from list_reaction_frames. Use this for any frame, including ones a person made in the dashboard, which have no generationId. Pass exactly one of the two.",
      "type": "string",
      "minLength": 1
    }
  },
  "required": [
    "workspace"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#"
}
Example response
{
  "success": true,
  "data": {
    "workspace": {
      "id": "6a18c36ee586c7e44e4ece67",
      "name": "Client A",
      "slug": "client-a"
    },
    "generation": {
      "id": "gen_frame_aa11bb22",
      "kind": "reaction_frame",
      "state": "running",
      "phase": "generating-frames",
      "input": {
        "videoId": "vid_aa11bb22",
        "source": "template",
        "imageModel": "gemini_flash"
      },
      "outputs": [],
      "dashboardUrl": "https://ghostfeed.ai/w/client-a/ugc-reactions?ref=agent",
      "createdAt": "2026-07-18T12:01:00.000Z",
      "updatedAt": "2026-07-18T12:07:00.000Z"
    },
    "nextSteps": "Poll until it succeeds with a fresh frame, then get approval before the video."
  }
}