PhotoGPT Developers

Generate creator-style product stills, upscale and polish one, then animate it into a short UGC video.

UGC Product Video

Use this workflow when you want a creator-style product ad. It starts from your trained creator model, produces a recognizable product still, upscales the best still, improves it with a known image model, and uses the final image as the first frame for a short video.

Before you start

Complete Setup and Training first. These snippets assume modelId or model_id, triggerImageGeneration, triggerImageUpscaling, triggerVideoGeneration, waitForJob, and firstImage are already available.

Workflow

  1. Generate a product still with POST /images/generation.
  2. Upscale the selected still with POST /images/upscaling.
  3. Create a polished ad still with POST /images/generation.
  4. Animate the polished still with POST /videos/generation.

Generate the creator product still

This uses the trained model ID, so the generated image should preserve the trained identity while following the product prompt.

const trainedImageJobId = await triggerImageGeneration({
  modelID: modelId,
  prompt:
    'authentic UGC product photo, creator holding a skincare bottle near a bathroom mirror, natural phone camera lighting',
  width: 1024,
  height: 1024,
  numImages: 2,
  enhanceFace: true,
})

const trainedImage = firstImage(await waitForJob(trainedImageJobId), 'UGC trained image')
trained_image_job_id = trigger_image_generation(
    {
        "modelID": model_id,
        "prompt": "authentic UGC product photo, creator holding a skincare bottle near a bathroom mirror, natural phone camera lighting",
        "width": 1024,
        "height": 1024,
        "numImages": 2,
        "enhanceFace": True,
    }
)

trained_image = first_image(wait_for_job(trained_image_job_id), "UGC trained image")

Upscale the selected still

Upscale the best generated still before using it as a reference image for a cleaner campaign version.

const upscaleJobId = await triggerImageUpscaling({
  imageID: trainedImage.id,
})

const upscaledImage = firstImage(await waitForJob(upscaleJobId), 'UGC upscale')
upscale_job_id = trigger_image_upscaling(
    {
        "imageID": trained_image["id"],
    }
)

upscaled_image = first_image(wait_for_job(upscale_job_id), "UGC upscale")

Improve the still with a public image model

Use a public image model such as gpt-image-2 for a more polished ad still. The upscaled image becomes the visual reference.

const polishedImageJobId = await triggerImageGeneration({
  modelID: 'gpt-image-2',
  prompt:
    'polished social ad still, creator holding the skincare bottle, clean bathroom counter, premium but natural UGC style',
  aspectRatio: '16:9',
  numImages: 1,
  referenceImageURLs: [upscaledImage.url],
  options: {
    openai: {
      imageSize: '1K',
      outputFormat: 'jpeg',
      quality: 'high',
    },
  },
})

const polishedImage = firstImage(await waitForJob(polishedImageJobId), 'UGC polished image')
polished_image_job_id = trigger_image_generation(
    {
        "modelID": "gpt-image-2",
        "prompt": "polished social ad still, creator holding the skincare bottle, clean bathroom counter, premium but natural UGC style",
        "aspectRatio": "16:9",
        "numImages": 1,
        "referenceImageURLs": [upscaled_image["url"]],
        "options": {
            "openai": {
                "imageSize": "1K",
                "outputFormat": "jpeg",
                "quality": "high",
            },
        },
    }
)

polished_image = first_image(wait_for_job(polished_image_job_id), "UGC polished image")

Generate the UGC video

Use the polished image as first_frame so the video starts from the campaign still.

const videoJobId = await triggerVideoGeneration({
  modelID: 'seedance-2.0',
  prompt:
    'short UGC product video, creator lifts the skincare bottle, smiles, and points to the label, handheld phone camera energy',
  numVideos: 1,
  referenceImages: [
    {
      url: polishedImage.url,
      role: 'first_frame',
    },
  ],
  options: {
    seedance: {
      duration: 6,
      ratio: '16:9',
      resolution: '1080p',
      generateAudio: false,
    },
  },
})

const videoJob = await waitForJob(videoJobId, 8000)
const video = videoJob.videos?.[0]
video_job_id = trigger_video_generation(
    {
        "modelID": "seedance-2.0",
        "prompt": "short UGC product video, creator lifts the skincare bottle, smiles, and points to the label, handheld phone camera energy",
        "numVideos": 1,
        "referenceImages": [
            {
                "url": polished_image["url"],
                "role": "first_frame",
            },
        ],
        "options": {
            "seedance": {
                "duration": 6,
                "ratio": "16:9",
                "resolution": "1080p",
                "generateAudio": False,
            },
        },
    }
)

video_job = wait_for_job(video_job_id, interval_seconds=8)
video = video_job.get("videos", [])[0]

Production notes

  • Store the generated image.id values so you can upscale, inspect, or reuse assets later.
  • Keep the product prompt specific about object placement, hand position, and scene style.
  • Use referenceImageURLs for known-image-model workflows and referenceImages with role: "first_frame" for image-to-video workflows.

On this page