> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mavera.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Sora Video Generation → Analysis Loop

> Generate video scripts with Mavera, create video with Sora, and iterate through Mavera quality assessment until thresholds are met

## Scenario

Use Mavera to generate a video script, feed it to OpenAI's Sora to generate video, then loop results through Mavera's Mave Agent for quality assessment — brand alignment, narrative coherence, visual impact — and iterate until quality thresholds are met.

**Flow:** Mavera `POST /generations` (script) → OpenAI Sora `POST /images/generations` → Mavera `POST /mave/chat` (analyze) → Iterate

<Warning>
  Sora's API is actively evolving. The example below uses the `images.generate` endpoint with `model="sora"`, which reflects the current API surface. Check [OpenAI's Sora documentation](https://platform.openai.com/docs/guides/video-generation) for the latest endpoint details.
</Warning>

## Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time, base64, re
  from openai import OpenAI

  client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
  MV = os.environ["MAVERA_API_KEY"]
  MV_BASE = "https://app.mavera.io/api/v1"
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  os.makedirs("sora_output", exist_ok=True)
  MAX_ITERATIONS = 3
  QUALITY_THRESHOLD = 8

  # 1. Generate video script with Mavera
  script_gen = requests.post(f"{MV_BASE}/generations", headers=MV_H, json={
      "prompt": "Write a detailed 15-second video prompt for Sora. "
          "Product: SaaS marketing analytics dashboard. "
          "Style: Clean, modern, cinematic. Show the dashboard in use — "
          "charts animating upward, a milestone notification popping. "
          "End on a wide shot with the tagline visible. "
          "Include camera angles, lighting, transitions.",
  }).json()
  script = script_gen.get("output") or script_gen.get("content") or ""
  print(f"Script generated: {len(script)} chars")
  time.sleep(2)

  for iteration in range(1, MAX_ITERATIONS + 1):
      print(f"\n{'='*60}\nITERATION {iteration}\n{'='*60}")

      # 2. Generate video with Sora
      try:
          sora_resp = client.images.generate(model="sora", prompt=script, n=1, size="1792x1024")
          video_url = sora_resp.data[0].url
          video_b64 = sora_resp.data[0].b64_json
          output_path = f"sora_output/video_v{iteration}.mp4"

          if video_b64:
              with open(output_path, "wb") as f:
                  f.write(base64.b64decode(video_b64))
              print(f"Video saved: {output_path} ({os.path.getsize(output_path) // 1024} KB)")
          elif video_url:
              vid_data = requests.get(video_url).content
              with open(output_path, "wb") as f:
                  f.write(vid_data)
              print(f"Video downloaded: {output_path} ({len(vid_data) // 1024} KB)")
      except Exception as e:
          print(f"Sora error: {e} — retrying...")
          time.sleep(5)
          continue

      time.sleep(2)

      # 3. Analyze with Mavera
      analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
          "message": f"Video quality assessor. Iteration {iteration}.\n\n"
              f"SCRIPT:\n{script[:3000]}\n\n"
              "Score 1-10 on: BRAND ALIGNMENT, NARRATIVE COHERENCE, "
              "VISUAL QUALITY, EMOTIONAL IMPACT, CTA CLARITY.\n"
              "Give OVERALL SCORE (1-10) and SPECIFIC IMPROVEMENTS as a refined Sora prompt.",
      }).json()

      analysis_text = analysis.get("content", "")
      print(analysis_text[:2000])

      score_line = [l for l in analysis_text.split("\n") if "OVERALL" in l.upper()]
      nums = re.findall(r'\b(\d+)\b', score_line[0]) if score_line else []
      score = int(nums[0]) if nums else 0
      print(f"\nScore: {score}/10 (threshold: {QUALITY_THRESHOLD})")

      if score >= QUALITY_THRESHOLD:
          print(f"Quality met at iteration {iteration}. Final: {output_path}")
          break

      # 4. Refine prompt
      refined = [l for l in analysis_text.split("\n") if "prompt" in l.lower() or "refine" in l.lower()]
      if refined:
          script += "\n\nRefinements: " + " ".join(refined)[:500]
      time.sleep(3)
  else:
      print(f"Max iterations ({MAX_ITERATIONS}) reached.")
  ```

  ```javascript JavaScript theme={"dark"}
  import OpenAI from "openai";
  import fs from "fs";

  const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
  const MV = process.env.MAVERA_API_KEY;
  const MV_BASE = "https://app.mavera.io/api/v1";
  const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
  fs.mkdirSync("sora_output", { recursive: true });

  const scriptGen = await fetch(`${MV_BASE}/generations`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ prompt: "Write a detailed 15-second Sora video prompt. Product: SaaS analytics "
      + "dashboard. Clean, cinematic. Dashboard in use, charts animating. End with tagline." }),
  }).then(r => r.json());
  let script = scriptGen.output || scriptGen.content || "";
  await new Promise(r => setTimeout(r, 2000));

  for (let iter = 1; iter <= 3; iter++) {
    const outPath = `sora_output/video_v${iter}.mp4`;
    try {
      const resp = await client.images.generate({ model: "sora", prompt: script, n: 1, size: "1792x1024" });
      const data = resp.data[0];
      if (data.b64_json) fs.writeFileSync(outPath, Buffer.from(data.b64_json, "base64"));
      else if (data.url) fs.writeFileSync(outPath, Buffer.from(await (await fetch(data.url)).arrayBuffer()));
      console.log(`Video saved: ${outPath}`);
    } catch (e) { console.log(`Sora error: ${e.message}`); await new Promise(r => setTimeout(r, 5000)); continue; }
    await new Promise(r => setTimeout(r, 2000));

    const analysis = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
      body: JSON.stringify({ message: `Video assessor. Iteration ${iter}.\nSCRIPT:\n${script.slice(0, 3000)}\n\n`
        + "Score 1-10: BRAND ALIGNMENT, NARRATIVE, QUALITY, IMPACT, CTA. OVERALL SCORE + IMPROVEMENTS." }),
    }).then(r => r.json());

    const text = analysis.content || "";
    console.log(text.slice(0, 2000));
    const score = parseInt((text.split("\n").find(l => /OVERALL/i.test(l))?.match(/\b(\d+)\b/) || [])[1] || "0");
    if (score >= 8) { console.log(`Quality met. Final: ${outPath}`); break; }
    const refined = text.split("\n").filter(l => /prompt|refine/i.test(l));
    if (refined.length) script += "\n\nRefinements: " + refined.join(" ").slice(0, 500);
    await new Promise(r => setTimeout(r, 3000));
  }
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
Script: 487 chars

ITERATION 1 → video_v1.mp4 (2,814 KB)
BRAND ALIGNMENT: 7/10 — Colors muted, should be #2563EB
NARRATIVE: 6/10 — Chart animation starts too early
QUALITY: 8/10 — Clean, no artifacts
IMPACT: 6/10 — Needs faster transitions
CTA: 5/10 — Tagline too small
OVERALL: 6/10 — Prompt refined for iteration 2

ITERATION 2 → video_v2.mp4 (3,102 KB)
OVERALL: 8/10 — Colors corrected, CTA prominent, timing fixed.
Quality met at iteration 2.
```

## Error Handling

<AccordionGroup>
  <Accordion title="Sora API availability">Sora access may require a specific tier. If you get a 404 or model-not-found error, verify access at [platform.openai.com/settings](https://platform.openai.com/settings). Fall back to DALL-E 3 for static frames if unavailable.</Accordion>
  <Accordion title="Generation timeouts">Video generation can take 30-120 seconds. Set HTTP client timeout to at least 180 seconds. The OpenAI SDK handles this automatically, but raw `requests` calls need `timeout=180`.</Accordion>
  <Accordion title="Iteration loop safeguards">The loop caps at 3 iterations to prevent runaway costs. Each iteration consumes Sora credits plus Mavera credits. If scores plateau, review the base script rather than continuing to refine.</Accordion>
</AccordionGroup>
