Developer API: Generate Creator UGC Videos from Your Own App
Connect your product to ppl.studio and produce creator videos programmatically—send a brief, get back a finished MP4 URL. No browser, no manual steps.
ppl.studio's Creator Video pipeline runs behind a small HTTP API. Your app sends a brief and a creator (a UGC persona); ppl.studio writes the script, generates the frames, and—once you approve the preview—animates them with Veo, assembles the cut, and hands you a hosted MP4. Everything you make is owned by your account and shows up in your gallery.
What you can integrate today
One rule explains the whole surface: a per-user API key authorizes the /api/v1/* endpoints and resolves to your account. Any persona or product you reference must be owned by that same account.
- Run any workflow (brief → preview → MP4) — available via API
- Approve the preview, poll the run — available via API
- Creators (personas) — create them in the dashboard or over the API (generate one from a niche), then reference by slug — available via API
- Products, uploads, photo packs, scripts, storyboards, social posting — available via API; the API overview maps all 144 endpoints
How it works: a run that stops at the preview
A creator video isn't a single render. The brief becomes a script, the script becomes first frames, and only then are the frames animated with Veo and cut together. The API is a run of the workflow: you start it and get a runId at once, and a worker moves it along step by step.
Before any video is made, the run stops at the preview—every frame with the line said over it, and what the video will cost on your key. You approve it (or start the run with straightThrough: true to skip the stop for that run), and it carries on to the finished cut.
Step 1 — Create your creator
A persona supplies the face and voice of every video and must be owned by your account. Create it in the ppl.studio dashboard—see Create Your First AI Expert—or over the API with POST /api/v1/personas/generate { "niche": "…", "save": true }, and note its slug (e.g. alex-reed). You can have many. Reference the slug on every video request.
Step 2 — Mint an API key
Open Account → API keys, create a key, and copy it immediately—the raw key (ppl_live_…) is shown once; only its hash is stored. Send it on every request as a header, either form works:
x-api-key: ppl_live_xxxxxxxxxxxxxxxxxxxx
# or
Authorization: Bearer ppl_live_xxxxxxxxxxxxxxxxxxxxStep 3 — Start a run
POST /api/v1/flows with your key, the workflow, a creator slug and a brief:
POST /api/v1/flows
x-api-key: ppl_live_xxxxxxxxxxxxxxxxxxxx
content-type: application/json
{
"preset": "creator-ugc-video",
"creatorSlug": "alex-reed",
"brief": { "niche": "3-second fixes for a messy kitchen", "goal": "reach", "platform": "tiktok" }
}
// → 201 { "ok": true, "data": { "runId": "…" } }Instead of a brief you can send briefSource: { mode: "url", value: "https://your-product-page" }—the run reads the page into a brief first. Other workflows run the same way (testimonial-video, lipsync-ugc-video, animated-story-ad, the photo packs…); the API reference lists them.
Step 4 — Approve the preview, collect the video
Poll GET /api/v1/flows/{runId} every ~15–30s. When awaiting is gate, the frames and their lines are in frames and the price in estimate; POST /api/v1/flows/{runId}/approve makes the video. When finishedAt is set, the video is cut.url. A failed step says why in steps[].error; POST …/steps with its index tries it again.
Drop-in client (Node)
Start a run, approve the preview by your own rule, and await the URL:
const BASE = "https://people.voiceherald.com";
const KEY = process.env.PPL_API_KEY; // ppl_live_…
const api = (path, init = {}) =>
fetch(`${BASE}${path}`, { ...init, headers: { "content-type": "application/json", "x-api-key": KEY } })
.then(async (r) => { const b = await r.json(); if (!r.ok || !b.ok) throw new Error(b.error ?? r.status); return b.data; });
async function createCreatorVideo({ creatorSlug, brief, approve = (run) => true }) {
const { runId } = await api("/api/v1/flows", { method: "POST", body: JSON.stringify({ preset: "creator-ugc-video", creatorSlug, brief }) });
for (;;) {
await new Promise((s) => setTimeout(s, 20000));
const run = await api(`/api/v1/flows/${runId}`);
if (run.finishedAt) return run.cut.url;
if (run.steps.some((s) => s.status === "failed")) throw new Error("a step failed");
// The preview: every frame with its line, and what the video will cost.
if (run.awaiting === "gate" && approve(run)) await api(`/api/v1/flows/${runId}/approve`, { method: "POST" });
}
}
// Usage
const url = await createCreatorVideo({
creatorSlug: "alex-reed",
brief: { niche: "3-second fixes for a messy kitchen", goal: "reach", platform: "tiktok" },
approve: (run) => run.estimate.usd < 2, // your own spend rule
});
console.log("video ready:", url);Conventions & errors
Every /api/v1/* response is one of:
{ "ok": true, "data": { … } }
{ "ok": false, "error": "human-readable message" }400— an unknown workflow, or a creator or product that isn't yours401— missing, invalid, or revoked key404— run not found500— server error (safe to retry the POST)
Good to know
- Veo is nearly all of a video's cost; the preview's
estimateis what the clips will cost on your key, before you approve them. - The v1 POST doesn't honor an
Idempotency-Keyyet—dedupe retries on your side. - There are no webhooks—poll the run.
What's next?
Build the pieces around the API:
- Create Your First AI Expert — build the persona your videos use
- Animate: Talking-Head Videos — the same pipeline, in the app
- The AI UGC API — everything else the key can do: photo packs, products, uploads, scripts, storyboards, social posting
- The MCP server — the same API from Claude Code, Claude Desktop or Cursor, with results saved to your disk
Start building
Create a creator, mint an API key, and ship your first programmatic video in an afternoon.
Get your API key10 free photos · no credit card required
Founder of ppl.studio. Building AI tools for product marketing teams who need visual content at scale without the production overhead.