API Reference

Generate UGC videos, list actors, and check job status via REST API.

Download OpenAPI Spec·Import into Postman / Insomnia: agent-media.ai/openapi.json

Getting Started

Zero to first video in 5 minutes.

1. Get an API Key

Create one in your dashboard settings. Starts with ma_.

2. List Actors

Public endpoint — no API key required.

curl "https://api.agent-media.ai/v1/actors?limit=5"

3. Generate a Video

curl -X POST https://api.agent-media.ai/v1/generate/ugc_video \
  -H "Authorization: Bearer ma_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "script": "Have you ever struggled with creating video content? This tool changed everything.",
    "actor_slug": "sofia",
    "tone": "energetic",
    "style": "hormozi",
    "target_duration": 10
  }'

4. Poll for Status

curl https://api.agent-media.ai/v1/videos/{job_id} \
  -H "Authorization: Bearer ma_YOUR_KEY"

# When status: "completed", video_url has your MP4.

Authentication

Generate, status, and account endpoints require a Bearer token. The actor library (/v1/actors) is public and needs no token. Two token types are accepted on protected endpoints:

  • API key (ma_…) — server-to-server calls. Create one in dashboard settings.
  • Supabase JWT — user-bound session token issued by Supabase Auth. Use this from web apps and especially mobile apps.
Authorization: Bearer ma_YOUR_API_KEY
# or
Authorization: Bearer eyJhbGc...   # Supabase JWT

Mobile App Auth

Building an iOS or Android app? Use Supabase Auth + JWT, not ma_ API keys. A static API key embedded in your app binary can be extracted from the IPA/APK in minutes — treat anything compiled into the client as public.

Recommended flow

  1. User signs in via Supabase Auth (Apple Sign-In, Google, email/password).
  2. Supabase issues an access token (JWT) and a refresh token, stored in iOS Keychain / Android Keystore by the SDK.
  3. App calls https://api.agent-media.ai/v1/... with Authorization: Bearer <access_token>.
  4. SDK refreshes the JWT automatically before expiry. On logout, SDK clears tokens.
  5. Per-user rate limits, credit balance, and quotas all key off the Supabase user id inside the JWT — no extra wiring needed.

Client SDKs

  • iOS: supabase-swift
  • Android: supabase-kt

In-app purchases

App Store and Play Store policy require their billing system for digital goods. Use StoreKit 2 (iOS) or Google Play Billing (Android) to sell credit packs and subscriptions in the mobile app, then verify the receipt server-side and credit the user's balance. Don't embed the Stripe checkout flow in the mobile app — Apple will reject the build.

Recommended hardening

  • Email verification required before first generation (Supabase Auth setting).
  • App Attest (iOS) / Play Integrity (Android) — cryptographically prove requests come from a genuine, unmodified app build. Blocks reverse-engineered API access and emulator/jailbreak abuse. See /functions/v1/verify-attestation.
  • HTTPS only, certificate pinning if your threat model warrants it.

What you already get

  • Per-user rate limit: 10 generates/min, 60 reads/min — keyed on JWT user id.
  • Credit deduction is atomic with job creation; failures refund automatically.
  • One concurrent generation per user; later submissions queue server-side.
  • 402 + JSON error when the user is out of credits.

POST/v1/generate/ugc_video

Generate a UGC-style video from a script or prompt.

Parameters

NameTypeDescription
scriptstringNarration script (50-3000 chars). Required if no prompt.
promptstringAI generates script from this. Required if no script.
actor_slugstringAI actor slug. Use GET /v1/actors to browse.
toneenumenergetic, calm, confident, dramatic
stylestringSubtitle style (17 options). Default: hormozi
target_durationnumber5, 10, or 15 seconds
musicenumchill, energetic, corporate, dramatic, upbeat
aspect_ratioenum9:16, 16:9, 1:1
allow_brollbooleanEnable B-roll cutaway scenes
broll_imagesstring[]Image URLs for B-roll (max 10)
webhook_urlstringHTTPS URL for completion callback
ctastringEnd screen CTA (max 100 chars)

POST/v1/generate/show_your_app

AI actor holds a phone that shows your app screenshot while reading your script, with Hormozi-style word-by-word subtitles burned in. Powered by GPT Image 2 + Seedance 2.0.

Parameters

NameTypeDescription
app_screenshot_urlstringPublic URL of a vertical (portrait) app screenshot — PNG, JPEG, or WebP. Required.
scriptstringWhat the actor reads (5–3000 chars, capped at 3 words/sec × duration). Required.
actor_slugstringSpecific actor slug. Random if omitted.
durationnumber5, 10, or 15 seconds. Default 5.
subtitle_styleenumhormozi or none. Default hormozi.
webhook_urlstringHTTPS URL for completion callback.

Example

curl -X POST https://api.agent-media.ai/v1/generate/show_your_app \
  -H "Authorization: Bearer ma_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "app_screenshot_url": "https://cdn.example.com/my-app.png",
    "script": "You really need to try this — it generates UGC videos in seconds.",
    "duration": 5
  }'

# Response:
# { "job_id": "...", "actor_slug": "sarah", "actor_random": true, "credits_deducted": 75, "subtitle_style": "hormozi" }

Credit cost: 75 flat. Typical runtime: 4–8 minutes.


POST/v1/generate/laptop_ugc

3-scene UGC ad: an AI actor holds an open laptop showing your app, then a scrolling B-roll over-shoulder shot, then a face-only selfie close. Native lip-synced audio + Hormozi-style burnt subtitles. Powered by GPT Image 2 + Seedance 2.0.

Parameters

NameTypeDescription
app_screen_recording_urlstringPublic URL of a screen recording of your app — mp4 or mov, max 10 MB, max 10 seconds. Provide this OR app_screen_image_url, not both.
app_screen_image_urlstringPublic URL of a static screenshot — png, jpeg, or webp, max 5 MB. Provide this OR app_screen_recording_url, not both.
scriptstringVoiceover script (5–3000 chars, capped at 3 words/sec × duration). Auto-split at sentence boundary between two face shots.
actor_slugstringSpecific actor slug. Random if omitted.
durationnumber15 or 20 seconds. Default 20.
subtitle_styleenumhormozi or none. Default hormozi.
webhook_urlstringHTTPS URL for completion callback.

Example

curl -X POST https://api.agent-media.ai/v1/generate/laptop_ugc \
  -H "Authorization: Bearer ma_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "app_screen_recording_url": "https://cdn.example.com/my-app-demo.mp4",
    "script": "Found this AI tool that ships UGC ads in 2 minutes from my terminal. Run it from CLI or Claude Code. It is agent-media.",
    "duration": 20
  }'

# Response:
# { "job_id": "...", "actor_slug": "mei", "actor_random": true, "credits_deducted": 200, "subtitle_style": "hormozi" }

Credit cost: 200 flat. Typical runtime: 8–12 minutes (first job per actor adds ~3 min for pose pre-generation).


POST/v1/generate/product_acting_ugc

Generate a product-in-hand UGC video: an AI creator holds, presents, or reacts to your product image in a real-world social ad scenario. Subtitles are generated from the final audio with word-level timestamps.

Parameters

NameTypeDescription
product_image_urlstringPublic URL of the product image. Required.
actor_slugstringActor slug from GET /v1/actors. Required.
actor_variant_iduuidOptional specific actor look/variant.
product_namestringOptional product name for prompt and script context.
product_descriptionstringProduct context. Required if script is omitted.
scriptstringExact words the actor says. Optional if product_description is provided.
templateenumproduct-in-hand, mirror-selfie, bathroom-reaction, kitchen-counter, car-selfie, couch-review, expert-interview, product-closeup.
acting_styleenumraw-selfie, shocked, angry, excited, dramatic, weird-hook, casual-demo, honest-review.
visual_stylestringExtra camera, pose, environment, or framing direction.
durationnumber5, 10, or 15 seconds. Default 5.
subtitle_styleenumhormozi or none. Default hormozi.
webhook_urlstringHTTPS URL for completion callback.

Example

curl -X POST https://api.agent-media.ai/v1/generate/product_acting_ugc \
  -H "Authorization: Bearer ma_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "product_image_url": "https://cdn.example.com/perfume.png",
    "actor_slug": "sarah",
    "product_name": "Rose Noir",
    "product_description": "Premium rose perfume with a warm vanilla dry-down.",
    "template": "product-in-hand",
    "acting_style": "honest-review",
    "duration": 5,
    "subtitle_style": "hormozi"
  }'

# Response:
# { "job_id": "...", "actor_slug": "sarah", "credits_deducted": 205, "subtitle_style": "hormozi" }

Credit cost: 30 credits/sec + 50 frame credits + 5 script credits when script is generated.


PIPELINECharacter Video — 3 steps

Build a fully-realised character clip end-to-end: (1) generate a multi-angle character sheet from a description, an actor, or a portrait; (2) paint a numbered storyboard from beats or a script; (3) render a 720p Seedance 2.0 video using both as references. Each step is async — the route returns 202 with a job_id; poll GET /v1/videos/{job_id} until status="completed". Pass the same session_id UUID across all three calls so step 3 can backstop the scene prompt from step 2.

Step 1 — POST /v1/character/sheet-generate

Generates a 3:2 landscape character reference sheet via gpt-image-2.

NameTypeDescription
descriptionstringFree-text character description (3–400 chars). Provide alone for "describe a character" mode (worker paints a portrait first, then turns it into the sheet).
actor_slugstringActor from the agent-media library. Mutually exclusive with reference_image_url. List via GET /v1/actors.
reference_image_urlstringPublic HTTPS URL of a portrait (PNG/JPEG/WebP, ≤5 MB recommended). Mutually exclusive with actor_slug.
session_iduuidOptional. Same UUID across all 3 steps makes inserts idempotent.

Credit cost: 20 credits ($0.20) for actor / reference (1 gpt-image-2 call, 75% margin), 35 credits ($0.35) for description-only (paint portrait + sheet = 2 calls, 71% margin). Typical runtime: 2–4 min.

Step 1.5 (optional) — POST /v1/character/storyboard-suggest

Returns 3 distinct beat-sequence options written by Claude Opus 4.7 (different vibes / scenarios). Sync; no credits.

NameTypeDescription
actor_slugstringLibrary actor whose persona drives the suggestions. Mutually exclusive with character_description.
character_descriptionstring1–200 chars. Mutually exclusive with actor_slug.
vibestringOptional 1–200 char vibe note (e.g. "wholesome", "chaotic").
durationnumber5 or 10. Default 10.
n_panelsnumber4–10. Default 6.

Returns { options: [{ title, beats[] }, …] } — pick one option's beats array and pass it to step 2.

Step 2 — POST /v1/character/storyboard-generate

Paints a numbered storyboard panel grid from beats or a free-text script. Async.

NameTypeDescription
character_sheet_urlstringRequired. Public HTTPS URL from step 1 (or your own).
beatsstring[]3–10 short beat descriptions, one per panel. Mutually exclusive with script.
scriptstringFree-text script (10–1500 chars). gpt-image-2 splits it into 4–6 panels itself. Mutually exclusive with beats.
ratioenum9:16, 16:9, or 1:1. Default 9:16.
session_iduuidSame UUID as step 1 for idempotency.

Credit cost: 20 credits ($0.20) — 75% margin on $0.05 gpt-image-2 cost. Typical runtime: 2–4 min.

Step 3 — POST /v1/generate/character_video

Renders the final clip via Seedance 2.0 reference-to-video (720p locked).

NameTypeDescription
character_sheet_urlstringRequired. URL from step 1.
storyboard_urlstringRequired. URL from step 2.
action_promptstringOptional scene/action description (the location, dialogue, what the character does). When omitted, api-v2 backstops it from the same session's storyboard script/beats — without scene context Seedance picks the location arbitrarily.
durationnumber5 or 10 seconds. Default 10. Max 10 — Seedance i2v quality drops past 10s with multimodal inputs.
aspect_ratioenum9:16, 16:9, or 1:1. Default 9:16.
generate_audiobooleanWhether Seedance synthesizes synchronized audio. Default true.
session_iduuidSame UUID as steps 1+2.
webhook_urlstringHTTPS URL for completion callback.

Credit cost: 350 credits ($3.50) for 5s, 700 credits ($7.00) for 10s — both 72% margin on EvoLink Seedance 720p ($0.199/sec). Typical render: 90–180s.

End-to-end example

# Step 1 — character sheet (returns 202 + job_id)
SESSION=$(uuidgen)
curl -X POST https://api.agent-media.ai/v1/character/sheet-generate \
  -H "Authorization: Bearer ma_YOUR_KEY" -H "Content-Type: application/json" \
  -d "{\"description\":\"Marco, 35yo Italian chef, white uniform, curly black hair\",\"session_id\":\"$SESSION\"}"

# Poll until completed
curl -H "Authorization: Bearer ma_YOUR_KEY" \
  https://api.agent-media.ai/v1/videos/<job_id>

# Step 2 — storyboard from a script
curl -X POST https://api.agent-media.ai/v1/character/storyboard-generate \
  -H "Authorization: Bearer ma_YOUR_KEY" -H "Content-Type: application/json" \
  -d "{
    \"character_sheet_url\": \"<step1 output URL>\",
    \"script\": \"Marco walks into his sunlit Brooklyn kitchen, takes a bite of fresh bread, smiles.\",
    \"ratio\": \"9:16\",
    \"session_id\": \"$SESSION\"
  }"

# Step 3 — final video
curl -X POST https://api.agent-media.ai/v1/generate/character_video \
  -H "Authorization: Bearer ma_YOUR_KEY" -H "Content-Type: application/json" \
  -d "{
    \"character_sheet_url\": \"<step1 output URL>\",
    \"storyboard_url\": \"<step2 output URL>\",
    \"duration\": 10,
    \"aspect_ratio\": \"9:16\",
    \"session_id\": \"$SESSION\"
  }"
# → { "job_id": "...", "credits_deducted": 400 }

Don't want to orchestrate three calls? The CLI runs the whole pipeline in one command: agent-media character-video --description "…" --script "…" --sync. MCP tools create_character_sheet, create_storyboard, and create_character_video expose the same steps to Claude Code, Cursor, and other MCP clients.


POST/v1/generate/saas_review

SaaS review video — talking head + product screenshots as B-roll. Provide a product_url and the AI writes the script and stitches the review automatically, or pass an explicit script and supply broll_images (1–3 product screenshots) yourself.

Parameters

NameTypeDescription
scriptstringNarration script (50–3000 chars). Required if no prompt and no product_url.
promptstringAI generates a script from this. Mutually exclusive with script. Costs 5 extra credits.
product_urlstringSaaS product URL. AI fetches context and generates the script.
angleenumhonest, enthusiastic, roast, tutorial, comparison.
actor_slugstringAI actor slug. Use GET /v1/actors. Random if omitted.
target_durationnumber5, 10, or 15 seconds.
stylestringSubtitle style (17 options). Default: hormozi.
toneenumenergetic, calm, confident, dramatic.
musicenumchill, energetic, corporate, dramatic, upbeat.
ctastringEnd-screen CTA (max 100 chars).
aspect_ratioenum9:16, 16:9, 1:1. Default 9:16.
allow_brollbooleanEnable AI-generated B-roll cutaway scenes.
broll_imagesstring[]Up to 10 product screenshot URLs to use as B-roll.
webhook_urlstringHTTPS URL for completion callback.

Example

curl -X POST https://api.agent-media.ai/v1/generate/saas_review \
  -H "Authorization: Bearer ma_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "product_url": "https://linear.app",
    "angle": "enthusiastic",
    "target_duration": 10,
    "actor_slug": "sofia"
  }'

# Response:
# { "job_id": "...", "status": "submitted", "credits_deducted": 305 }

Credit cost: 30 credits/sec + 5 script credits when AI generates the script.


POST/v1/generate/subtitle

Burn animated subtitles onto an existing video. Audio is transcribed with word-level timestamps and rendered in the chosen style.

Parameters

NameTypeDescription
video_urlstringPublic URL of the source video. Required.
styleenum17 styles: hormozi (default), minimal, bold, karaoke, clean, tiktok, neon, fire, glow, pop, aesthetic, impact, pastel, electric, boxed, gradient, spotlight.

Example

curl -X POST https://api.agent-media.ai/v1/generate/subtitle \
  -H "Authorization: Bearer ma_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://cdn.example.com/clip.mp4",
    "style": "hormozi"
  }'

# Response:
# { "job_id": "...", "status": "submitted", "credits_deducted": 50 }

Credit cost: 50 flat per video.


GET/v1/actors

Browse the actor library, filter by gender / age / persona / preset, search by name, or look up a single actor by slug. Public endpoint — no API key required. Rate limited to 60 requests/min per IP.

Query parameters

ParamValuesDescription
genderfemale · maleFilter by gender
age_range18-25 · 26-35 · 36-50 · 51+Filter by age range
actor_typeYoung Adult · Professional · Mom · Elder · CasualFilter by persona type
presetshow_your_app · …Filter by preset tag
searchstringCase-insensitive substring on name
slugstringSingle-actor lookup. Returns 404 with similar-slug suggestions when missing.
limit1–500 (default 500)Page size — default returns full active library in one call. Ignored when slug is set.
offsetinteger (default 0)Page offset (ignored when slug is set)

Examples

# List all (default page size 50)
curl "https://api.agent-media.ai/v1/actors"

# Filter and paginate
curl "https://api.agent-media.ai/v1/actors?gender=female&age_range=26-35&limit=20"

# Search by name
curl "https://api.agent-media.ai/v1/actors?search=aali"

# Look up a specific actor
curl "https://api.agent-media.ai/v1/actors?slug=aaliyah"

Response — list

{
  "actors": [
    {
      "id": "66ac63f6-1be0-4c70-9914-c8ed90208be0",
      "slug": "aaliyah",
      "name": "Aaliyah",
      "gender": "female",
      "age": 28,
      "age_range": "26-35",
      "nationality": "African American",
      "actor_type": "Professional",
      "style": "Confident, poised, thoughtful presence",
      "portrait_url": "https://.../portrait.png",
      "green_screen_url": "https://.../green-screen.png",
      "voice_id": "cgSgspJ2msm6clMCkdW9",
      "voice_gender": "female",
      "lip_sync_engine": "aurora",
      "preset": null,
      "presets": ["show_your_app"],
      "bio_occupation": "Financial Wellness Educator",
      "bio_story": "...",
      "bio_hobbies": ["home espresso", "early morning runs"],
      "status": "active"
    }
  ],
  "total": 200,
  "limit": 50,
  "offset": 0
}

Response — single (slug lookup)

{ "actor": { "id": "...", "slug": "aaliyah", "name": "Aaliyah", ... } }

404 — slug not found

{
  "error": {
    "code": "ACTOR_NOT_FOUND",
    "message": "Actor 'aaliya' not found. Did you mean: aaliyah?",
    "similar": ["aaliyah"]
  }
}

GET/v1/videos/{jobId}

Poll job status. Returns video_url when completed.

{ "job_id": "uuid", "status": "completed", "video_url": "https://...mp4" }

Webhooks

When a video generation job completes (or fails), agent-media sends an HTTP POST to your webhook_url. This lets you get notified immediately instead of polling the status endpoint.

How to use

Include webhook_url in your generate request body:

curl -X POST https://api.agent-media.ai/v1/generate/ugc_video \
  -H "Authorization: Bearer ma_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "script": "Your narration script here.",
    "actor_slug": "sofia",
    "webhook_url": "https://example.com/webhooks/agent-media"
  }'

Payload format

On success, the POST body contains:

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "video_url": "https://media.agent-media.ai/videos/550e8400.mp4",
  "credits_deducted": 300
}

On failure:

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "failed",
  "error_message": "Script exceeded maximum duration for selected actor."
}

Retry policy

If your endpoint throws or returns a non-2xx status code, agent-media makes up to 3 total delivery attempts with exponential backoff: a 1 s wait after the first failure and a 2 s wait after the second. After all attempts are exhausted the webhook is abandoned — you can still poll /v1/videos/{job_id} for the result. Only HTTPS URLs are accepted.

Security

Verify that incoming requests originate from agent-media. The simplest approach is to include a shared secret as a query parameter in your webhook URL (e.g. https://example.com/hook?secret=MY_TOKEN) and check it on every request.

Example: what the POST looks like

curl -X POST https://example.com/webhooks/agent-media?secret=MY_TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "job_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "completed",
    "video_url": "https://media.agent-media.ai/videos/550e8400.mp4",
    "credits_deducted": 300
  }'

Tip: use webhook.site to inspect webhook payloads while developing.


SDKs

TypeScript

import { AgentMedia } from '@agentmedia/sdk';
const client = new AgentMedia({ apiKey: 'ma_xxx' });

// Standard UGC video
const video = await client.createVideo({ script: '...', actor_slug: 'sofia' });
console.log(video.video_url);

// Show Your App — phone holding your app screenshot
const app = await client.createShowYourApp({
  app_screenshot_url: 'https://cdn.example.com/my-app.png',
  script: 'You really need to try this',
  duration: 5,
});
console.log(app.video_url, 'actor:', app.actor_slug);

Python

from agent_media import AgentMedia
client = AgentMedia(api_key="ma_xxx")

# Standard UGC video
video = client.create_video(script="...", actor_slug="sofia")
print(video["video_url"])

# Show Your App
app = client.create_show_your_app(
    app_screenshot_url="https://cdn.example.com/my-app.png",
    script="You really need to try this",
    duration=5,
)
print(app["video_url"], "actor:", app["actor_slug"])

Interactive API Explorer

Try endpoints below. Enter your API key in the auth field.