Making videos
Plan for free, render for 25 credits each, then download.
Plan
POST /videos/plan runs the same planner as the Studio and Batch pages, on the server. It renders nothing and is free.
| Field | Type | Meaning |
|---|---|---|
format | "hook-demo" | "one-caption" | The video's shape. See Concepts. |
count | integer | How many videos: at most 20, and no more than the credits pay for. |
persona_ids | uuid[] | Only these personas. Leave out for all usable ones. |
demo_ids | uuid[] | Only these demos. Leave out for all. |
lines | "all" | "own" | Library and own lines, or own only. |
music | boolean | Stock music under every video, or none. |
theme | "white" | "black" | "box" | "light-box" | Caption style. |
recipe_id | uuid | Start from a saved recipe. |
variant | integer | Ask for a different plan with the same settings (the app's "Re-plan"). |
The planner is deterministic: the same input and variant give the same drafts. The answer (note says why, when the plan has fewer drafts than asked; else null):
{
"drafts": [
{
"format": "hook-demo",
"pattern_id": "p07",
"hook_text": "POV: you finally found a notes app that keeps up",
"ending_text": "Acme Notes",
"clip_id": "…",
"hook_asset_id": null,
"demo_id": "…",
"ending_id": "…",
"track": { "id": "…", "source": "stock" },
"theme": "white",
"ai_label": true,
"keep_audio": false
}
],
"cost": 125,
"credits_after": 2225,
"note": null
}…or the blocker the app would show: no_demo, no_personas, no_lines, not_enough_credits, setup_incomplete.
Agents: show the founder the drafts and the cost, and render only after they agree.
Render
curl https://getuser.app/api/v1/videos \
-H "Authorization: Bearer $GETUSER_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "drafts": [ … ] }'1–20 drafts of one format, from the planner or edited by you (change a hook_text, swap a clip_id). 25 credits each are held now and charged when each video is ready. Several drafts are queued as one batch, in order; if one is refused, the ones before it stay queued, the ones after it are not tried, and refused says which and why:
{
"batch_id": "…",
"videos": [
{ "id": "…", "status": "queued" },
{ "id": "…", "status": "queued" }
],
"refused": { "index": 2, "code": "not_enough_credits", "message": "This needs 25 credits; you have 10." },
"credits_held": 50
}Wait for it
curl "https://getuser.app/api/v1/videos?batch_id=…" -H "Authorization: Bearer $GETUSER_API_KEY"status goes queued → rendering → ready (or failed, with error_code and error). A video usually takes 1–3 minutes; with a queue, longer. Check about every 30 seconds, plus 15 seconds for each video still waiting (at most every 5 minutes), and ask for a whole batch in one call. There are no webhooks yet.
Download
A ready video has poster_url and download_url, both valid for one hour. Read the video again for fresh links. GET /batches/{id} gives every link of a batch and a zip plan.
Retry and cancel
POST /videos/{id}/retry: try afailedvideo again. It holds 25 credits again, as in the app, and gives them back if it fails again.POST /videos/{id}/cancel: cancel a waiting video; its credits come back.POST /batches/{id}/cancel: cancel every waiting video of a batch.
Videos cannot be deleted through the API. When storage is full, rendering answers 422 storage_full; the founder deletes old videos at getuser.app/videos.