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.

FieldTypeMeaning
format"hook-demo" | "one-caption"The video's shape. See Concepts.
countintegerHow many videos: at most 20, and no more than the credits pay for.
persona_idsuuid[]Only these personas. Leave out for all usable ones.
demo_idsuuid[]Only these demos. Leave out for all.
lines"all" | "own"Library and own lines, or own only.
musicbooleanStock music under every video, or none.
theme"white" | "black" | "box" | "light-box"Caption style.
recipe_iduuidStart from a saved recipe.
variantintegerAsk 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 a failed video 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.

On this page