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

# Video

> Generate, animate, and continue video with FLUX 3. Up to full-HD and 20 seconds, with synchronized audio.

export const VideoExtension = ({src, poster, originalDuration = 5, aspectRatio = "16 / 9", accent = "#3ecf8e", alt = ""}) => {
  const videoRef = useRef(null);
  const [playing, setPlaying] = useState(true);
  const [muted, setMuted] = useState(true);
  const [dur, setDur] = useState(0);
  const [t, setT] = useState(0);
  const orig = dur ? Math.min(originalDuration, dur) : originalDuration;
  const split = dur ? Math.min(Math.max(orig / dur, 0.06), 0.94) : 0.5;
  const progress = dur ? Math.min(t / dur, 1) : 0;
  const inExt = dur > 0 && t >= orig - 0.05;
  const fmt = s => {
    s = Math.max(0, Math.round(s || 0));
    const m = Math.floor(s / 60);
    return `${m}:${String(s % 60).padStart(2, "0")}`;
  };
  const onMeta = e => setDur(e.currentTarget.duration || 0);
  const onTime = e => setT(e.currentTarget.currentTime || 0);
  const onPlay = () => setPlaying(true);
  const onPause = () => setPlaying(false);
  const togglePlay = () => {
    const v = videoRef.current;
    if (!v) return;
    if (v.paused) {
      const p = v.play?.();
      if (p && typeof p.catch === "function") p.catch(() => {});
    } else {
      v.pause();
    }
  };
  const toggleMute = () => {
    const v = videoRef.current;
    const next = !muted;
    setMuted(next);
    if (v) v.muted = next;
  };
  const seek = e => {
    const v = videoRef.current;
    if (!v || !dur) return;
    const r = e.currentTarget.getBoundingClientRect();
    const x = (e.clientX - r.left) / r.width;
    v.currentTime = Math.min(Math.max(x, 0), 1) * dur;
    setT(v.currentTime);
  };
  const neutral = a => `color-mix(in srgb, currentColor ${a}%, transparent)`;
  const iconBtn = {
    position: "absolute",
    top: "0.9rem",
    right: "0.9rem",
    zIndex: 4,
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    width: "2.4rem",
    height: "2.4rem",
    padding: 0,
    borderRadius: "999px",
    border: "none",
    background: "rgba(12, 14, 18, 0.62)",
    backdropFilter: "blur(6px)",
    color: "#fff",
    cursor: "pointer",
    transition: "background 160ms ease"
  };
  return <div className="not-prose" style={{
    margin: "0.5rem 0 0.25rem"
  }}>
      {}
      <div onClick={togglePlay} style={{
    position: "relative",
    width: "100%",
    aspectRatio,
    borderRadius: "0.9rem",
    overflow: "hidden",
    background: "#0c0e12",
    cursor: "pointer",
    boxShadow: `0 0 0 1px ${neutral(10)}`
  }}>
        <video ref={videoRef} src={src} poster={poster} autoPlay loop muted={muted} playsInline preload="metadata" aria-label={alt} onLoadedMetadata={onMeta} onTimeUpdate={onTime} onPlay={onPlay} onPause={onPause} style={{
    position: "absolute",
    inset: 0,
    width: "100%",
    height: "100%",
    objectFit: "cover",
    display: "block"
  }} />

        {}
        <div style={{
    position: "absolute",
    top: "0.9rem",
    left: "0.9rem",
    zIndex: 4,
    display: "inline-flex",
    alignItems: "center",
    gap: "0.45rem",
    padding: "0.32rem 0.7rem 0.32rem 0.6rem",
    borderRadius: "999px",
    fontSize: "0.72rem",
    fontWeight: 600,
    letterSpacing: "0.01em",
    color: "#fff",
    background: inExt ? `color-mix(in srgb, ${accent} 82%, #0c0e12)` : "rgba(12, 14, 18, 0.62)",
    backdropFilter: "blur(6px)",
    boxShadow: inExt ? `0 0 0 1px ${accent}, 0 6px 20px -6px ${accent}` : `0 0 0 1px ${neutral(14)}`,
    transition: "background 320ms ease, box-shadow 320ms ease"
  }}>
          <span style={{
    width: "0.5rem",
    height: "0.5rem",
    borderRadius: "999px",
    background: inExt ? "#fff" : accent,
    boxShadow: inExt ? "0 0 8px #fff" : `0 0 8px ${accent}`,
    transition: "background 320ms ease"
  }} />
          {inExt ? "FLUX 3 continuation" : "Original clip"}
        </div>

        {}
        <button type="button" onClick={e => {
    e.stopPropagation();
    toggleMute();
  }} aria-label={muted ? "Unmute" : "Mute"} style={iconBtn}>
          {muted ? <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
              <line x1="23" y1="9" x2="17" y2="15" />
              <line x1="17" y1="9" x2="23" y2="15" />
            </svg> : <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
              <path d="M15.54 8.46a5 5 0 0 1 0 7.07" />
              <path d="M19.07 4.93a10 10 0 0 1 0 14.14" />
            </svg>}
        </button>

        {}
        <div style={{
    position: "absolute",
    inset: 0,
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    zIndex: 3,
    pointerEvents: "none",
    opacity: playing ? 0 : 1,
    transition: "opacity 200ms ease",
    background: playing ? "transparent" : "rgba(0,0,0,0.18)"
  }}>
          <span style={{
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    width: "3.4rem",
    height: "3.4rem",
    borderRadius: "999px",
    background: "rgba(12, 14, 18, 0.66)",
    backdropFilter: "blur(6px)",
    boxShadow: `0 0 0 1px ${neutral(16)}`,
    color: "#fff"
  }}>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <path d="M8 5v14l11-7z" />
            </svg>
          </span>
        </div>
      </div>

      {}
      <div style={{
    marginTop: "0.85rem"
  }}>
        <div role="slider" aria-label="Scrub — original clip then FLUX 3 continuation" aria-valuemin={0} aria-valuemax={Math.round(dur)} aria-valuenow={Math.round(t)} onClick={seek} style={{
    position: "relative",
    height: "1.5rem",
    cursor: "pointer",
    display: "flex",
    alignItems: "center"
  }}>
          {}
          <div style={{
    position: "relative",
    width: "100%",
    height: "0.55rem",
    borderRadius: "999px",
    background: neutral(8),
    overflow: "visible"
  }}>
            {}
            <div style={{
    position: "absolute",
    left: 0,
    top: 0,
    bottom: 0,
    width: `${split * 100}%`,
    borderRadius: "999px 0 0 999px",
    background: neutral(16)
  }} />
            {}
            <div style={{
    position: "absolute",
    right: 0,
    top: 0,
    bottom: 0,
    width: `${(1 - split) * 100}%`,
    borderRadius: "0 999px 999px 0",
    background: `linear-gradient(90deg, color-mix(in srgb, ${accent} 42%, transparent), color-mix(in srgb, ${accent} 22%, transparent))`
  }} />
            {}
            <div style={{
    position: "absolute",
    left: 0,
    top: 0,
    bottom: 0,
    width: `${progress * 100}%`,
    borderRadius: "999px",
    background: neutral(24),
    transition: "width 120ms linear",
    pointerEvents: "none"
  }} />
            {}
            <div style={{
    position: "absolute",
    left: `${split * 100}%`,
    top: "-0.28rem",
    bottom: "-0.28rem",
    width: "2px",
    transform: "translateX(-1px)",
    background: accent,
    borderRadius: "2px",
    boxShadow: `0 0 8px ${accent}`
  }} />
            {}
            <div style={{
    position: "absolute",
    left: `${progress * 100}%`,
    top: "50%",
    width: "0.85rem",
    height: "0.85rem",
    marginLeft: "-0.425rem",
    marginTop: "-0.425rem",
    borderRadius: "999px",
    background: "#fff",
    boxShadow: `0 1px 6px rgba(0,0,0,0.45)${inExt ? `, 0 0 0 3px color-mix(in srgb, ${accent} 55%, transparent)` : ""}`,
    transition: "left 120ms linear, box-shadow 200ms ease",
    pointerEvents: "none"
  }} />
          </div>
        </div>

        {}
        <div style={{
    display: "flex",
    justifyContent: "space-between",
    alignItems: "center",
    marginTop: "0.55rem",
    fontSize: "0.78rem"
  }}>
          <span style={{
    display: "inline-flex",
    alignItems: "center",
    gap: "0.4rem",
    opacity: 0.75
  }}>
            <span style={{
    width: "0.65rem",
    height: "0.65rem",
    borderRadius: "3px",
    background: neutral(30)
  }} />
            Original {fmt(orig)}
          </span>
          <span style={{
    fontVariantNumeric: "tabular-nums",
    opacity: 0.55,
    fontSize: "0.74rem"
  }}>
            {fmt(t)} / {fmt(dur)}
          </span>
          <span style={{
    display: "inline-flex",
    alignItems: "center",
    gap: "0.4rem",
    color: accent,
    fontWeight: 600
  }}>
            <span style={{
    width: "0.65rem",
    height: "0.65rem",
    borderRadius: "3px",
    background: accent,
    boxShadow: `0 0 8px ${accent}`
  }} />
            FLUX 3 continuation +{fmt(Math.max(dur - orig, 0))}
          </span>
        </div>
      </div>
    </div>;
};

export const ClipSlider = ({items = []}) => {
  const [copied, setCopied] = useState(-1);
  const [inView, setInView] = useState(0);
  const [edges, setEdges] = useState({
    left: false,
    right: items.length > 1
  });
  const [muted, setMuted] = useState(true);
  const trackRef = useRef(null);
  const cardRefs = useRef([]);
  const measuredRef = useRef(false);
  const mutedRef = useRef(true);
  const videosRef = useRef([]);
  const observerRef = useRef(null);
  const getObserver = () => {
    if (observerRef.current) return observerRef.current;
    if (typeof IntersectionObserver === "undefined") return null;
    observerRef.current = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        const v = entry.target;
        if (entry.isIntersecting) {
          v.muted = mutedRef.current;
          const p = v.play?.();
          if (p && typeof p.catch === "function") p.catch(() => {});
        } else {
          v.pause?.();
        }
      });
    }, {
      threshold: 0.4
    });
    return observerRef.current;
  };
  const attachVideo = el => {
    if (!el) return;
    if (!videosRef.current.includes(el)) videosRef.current.push(el);
    const io = getObserver();
    if (io) io.observe(el);
  };
  const toggleMute = () => {
    const next = !muted;
    setMuted(next);
    mutedRef.current = next;
    videosRef.current.forEach(v => {
      if (v) v.muted = next;
    });
  };
  const updateEdges = el => {
    if (!el) return;
    const max = el.scrollWidth - el.clientWidth;
    const next = {
      left: el.scrollLeft > 4,
      right: el.scrollLeft < max - 4
    };
    setEdges(prev => prev.left === next.left && prev.right === next.right ? prev : next);
    const cards = cardRefs.current.filter(Boolean);
    if (cards.length) {
      let best = 0;
      let bestDist = Infinity;
      cards.forEach((card, i) => {
        const dist = Math.abs(card.offsetLeft - el.scrollLeft - 4);
        if (dist < bestDist) {
          bestDist = dist;
          best = i;
        }
      });
      setInView(prev => prev === best ? prev : best);
    }
  };
  const attachTrack = el => {
    trackRef.current = el;
    if (!el || measuredRef.current) return;
    measuredRef.current = true;
    if (typeof requestAnimationFrame !== "undefined") {
      requestAnimationFrame(() => updateEdges(el));
    } else {
      updateEdges(el);
    }
  };
  const scrollByCard = dir => {
    const el = trackRef.current;
    if (!el) return;
    const first = cardRefs.current.find(Boolean);
    const cardW = first ? first.offsetWidth + 20 : el.clientWidth * 0.8;
    el.scrollBy({
      left: dir * cardW,
      behavior: "smooth"
    });
  };
  const scrollToCard = idx => {
    const el = trackRef.current;
    const card = cardRefs.current[idx];
    if (!el || !card) return;
    el.scrollTo({
      left: card.offsetLeft - 4,
      behavior: "smooth"
    });
  };
  const startVideo = event => {
    const video = event.currentTarget;
    video.muted = mutedRef.current;
    const p = video.play?.();
    if (p && typeof p.catch === "function") p.catch(() => {});
  };
  const copy = (text, idx) => {
    try {
      navigator.clipboard.writeText(text);
      setCopied(idx);
      setTimeout(() => setCopied(c => c === idx ? -1 : c), 2000);
    } catch (e) {}
  };
  return <div className="not-prose clip-slider">
      <button type="button" className="clip-slider__arrow clip-slider__arrow--prev" onClick={() => scrollByCard(-1)} disabled={!edges.left} aria-label="Previous clip">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <polyline points="15 18 9 12 15 6" />
        </svg>
      </button>

      <div className="clip-slider__track" ref={attachTrack} onScroll={e => updateEdges(e.currentTarget)}>
        {items.map((item, idx) => <article key={idx} className="clip-slider__slide" ref={el => {
    cardRefs.current[idx] = el;
  }}>
            <div className="clip-slider__media">
              <video ref={attachVideo} src={item.video} poster={item.poster} loop muted playsInline preload="metadata" onLoadedData={startVideo} onCanPlay={startVideo} aria-label={item.alt || item.title || ""} className="clip-slider__media-el" />
              <button type="button" onClick={toggleMute} aria-label={muted ? "Unmute" : "Mute"} title={muted ? "Enable sound" : "Mute"} style={{
    position: "absolute",
    top: "0.6rem",
    right: "0.6rem",
    zIndex: 4,
    width: "2rem",
    height: "2rem",
    borderRadius: "999px",
    border: "none",
    background: "rgba(8, 18, 13, 0.5)",
    color: "#fff",
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    cursor: "pointer",
    backdropFilter: "blur(4px)",
    WebkitBackdropFilter: "blur(4px)"
  }}>
                {muted ? <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M11 5 6 9H2v6h4l5 4V5z" /><line x1="23" y1="9" x2="17" y2="15" /><line x1="17" y1="9" x2="23" y2="15" /></svg> : <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M11 5 6 9H2v6h4l5 4V5z" /><path d="M15.54 8.46a5 5 0 0 1 0 7.07" /><path d="M19.07 4.93a10 10 0 0 1 0 14.14" /></svg>}
              </button>
            </div>

            <div className="clip-slider__body">
              {item.title && <h4 className="clip-slider__title">{item.title}</h4>}
              {item.prompt && <div className="clip-slider__prompt">
                  <button type="button" className="clip-slider__copy" onClick={() => copy(item.prompt, idx)} aria-label={copied === idx ? "Prompt copied" : "Copy prompt"} title={copied === idx ? "Copied" : "Copy prompt"}>
                    {copied === idx ? "Copied!" : "Copy"}
                  </button>
                  <pre className="clip-slider__prompt-pre"><code>{item.prompt}</code></pre>
                </div>}
            </div>
          </article>)}
      </div>

      <button type="button" className="clip-slider__arrow clip-slider__arrow--next" onClick={() => scrollByCard(1)} disabled={!edges.right} aria-label="Next clip">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <polyline points="9 18 15 12 9 6" />
        </svg>
      </button>

      {items.length > 1 && <div className="clip-slider__dots">
          {items.map((item, idx) => <button key={idx} type="button" onClick={() => scrollToCard(idx)} aria-label={`Go to ${item.title || `clip ${idx + 1}`}`} className={`clip-slider__dot${idx === inView ? " is-active" : ""}`} />)}
        </div>}
    </div>;
};

export const MutedVideo = ({src, poster, alt = "", aspectRatio = "16 / 9", borderRadius = "0.75rem"}) => {
  const videoRef = useRef(null);
  const [muted, setMuted] = useState(true);
  const start = event => {
    const v = event.currentTarget;
    v.muted = muted;
    const p = v.play?.();
    if (p && typeof p.catch === "function") p.catch(() => {});
  };
  const toggleMute = () => {
    const v = videoRef.current;
    const next = !muted;
    setMuted(next);
    if (v) v.muted = next;
  };
  const iconBtn = {
    position: "absolute",
    top: "0.9rem",
    right: "0.9rem",
    zIndex: 3,
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    width: "2.4rem",
    height: "2.4rem",
    padding: 0,
    borderRadius: "999px",
    border: "none",
    background: "rgba(12, 14, 18, 0.62)",
    backdropFilter: "blur(6px)",
    color: "#fff",
    cursor: "pointer",
    transition: "background 160ms ease"
  };
  return <div className="not-prose" style={{
    position: "relative",
    width: "100%",
    aspectRatio,
    borderRadius,
    overflow: "hidden",
    background: "#0c0e12"
  }}>
      <video ref={videoRef} src={src} poster={poster} autoPlay loop muted={muted} playsInline preload="metadata" onCanPlay={start} onLoadedData={start} aria-label={alt} style={{
    position: "absolute",
    inset: 0,
    width: "100%",
    height: "100%",
    objectFit: "cover",
    display: "block"
  }} />
      <button type="button" onClick={toggleMute} aria-label={muted ? "Unmute" : "Mute"} style={iconBtn}>
        {muted ? <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
            <line x1="23" y1="9" x2="17" y2="15" />
            <line x1="17" y1="9" x2="23" y2="15" />
          </svg> : <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
            <path d="M15.54 8.46a5 5 0 0 1 0 7.07" />
            <path d="M19.07 4.93a10 10 0 0 1 0 14.14" />
          </svg>}
      </button>
    </div>;
};

<div className="flux3-video-page flux3-page">
  <div className="not-prose flux3-video-hero cinematic-hero-root">
    <img src="https://cdn.sanity.io/images/2gpum2i6/production/0c1bd71fb6eea128602cc9f999b8f95d975123bf-2400x1166.png" alt="FLUX 3 Motion from a single line artwork" className="flux3-video-hero__image" />
  </div>

  <div className="not-prose flux3-video-copy">
    <p className="flux3-video-copy__eyebrow">FLUX 3</p>
    <h1>Video</h1>

    <p>
      Generate, animate, and continue video with FLUX 3, with
      synchronized audio in the same call.
    </p>
  </div>

  ## Quickstart

  FLUX 3 video is asynchronous: you **submit** a request, get back a `polling_url`,
  and **poll** it until the clip is `Ready`. For the full field list and response
  schema, see the [API reference](/api_reference/flux3).

  <CodeGroup>
    ```python Python theme={null}
    import os, time, requests

    BFL_API_KEY = os.environ["BFL_API_KEY"]

    # 1. Submit — returns an id and a polling_url
    submit = requests.post(
        "https://api.bfl.ai/v1/flux-3-video",
        headers={"x-key": BFL_API_KEY, "Content-Type": "application/json"},
        json={
            "mode": "t2v",
            "prompt": "a fox running through dawn mist",
            "generate_audio": True,
        },
    ).json()

    # 2. Poll the returned URL until the job is Ready
    while True:
        time.sleep(2)
        result = requests.get(submit["polling_url"], headers={"x-key": BFL_API_KEY}).json()
        if result["status"] == "Ready":
            print(result["result"]["sample"])   # signed .mp4 URL
            break
        if result["status"] in ("Error", "Request Moderated", "Content Moderated"):
            raise RuntimeError(result["status"])
    ```

    ```bash cURL theme={null}
    # 1. Submit — returns an id and a polling_url
    curl -s -X POST https://api.bfl.ai/v1/flux-3-video \
      -H "x-key: $BFL_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "mode": "t2v",
        "prompt": "a fox running through dawn mist",
        "generate_audio": true
      }'

    # 2. Poll the polling_url from the response until "status": "Ready"
    curl -s "$POLLING_URL" -H "x-key: $BFL_API_KEY"
    ```
  </CodeGroup>

  ## Modes

  One `mode` per request; the rest of the request stays the same.

  * **Text-to-Video** (`t2v`) — prompt only.
  * **Image-to-Video** (`i2v`) — add `keyframes`: one image is the opening frame, `[a, b]` pins start and end, and up to ten frames pinned to timestamps become a storyboard.
  * **Video Continuation** (`v2v`) — add `start_video`; the clip continues from its final frames.

  <div className="not-prose" style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 15rem), 1fr))", gap: "1.25rem", margin: "1.25rem 0 0.5rem" }}>
    <div>
      <MutedVideo src="https://cdn.sanity.io/files/2gpum2i6/production/fc743c980a0259403ccec6c9a705f9e412e6f81b.mp4" alt="Text-to-video example" />

      <p style={{ margin: "0.6rem 0 0", fontSize: "0.9rem", opacity: 0.75 }}><code>t2v</code> · Generate a video with audio</p>
    </div>

    <div>
      <MutedVideo src="https://cdn.sanity.io/files/2gpum2i6/production/9b7469e7f349b4f26fe9809b9faa13567504aa84.mp4" alt="Image-to-video example" />

      <p style={{ margin: "0.6rem 0 0", fontSize: "0.9rem", opacity: 0.75 }}><code>i2v</code> · Animate an image and use multiple keyframes</p>
    </div>

    <div>
      <MutedVideo src="https://cdn.sanity.io/files/2gpum2i6/production/08c30d095b03c80ebec84e8a547c3ea694c2efca.mp4" alt="Video continuation example" />

      <p style={{ margin: "0.6rem 0 0", fontSize: "0.9rem", opacity: 0.75 }}><code>v2v</code> · Extend your clip</p>
    </div>
  </div>

  ## Parameters

  Full schema and per-field constraints live in the [API reference](/api-reference/utility/generate-a-video-with-flux-3).

  | Field              | Required            |                                                                                                            |
  | ------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------- |
  | `mode`             | always              | `t2v`, `i2v`, `v2v`, or `draft_enhance`.                                                                   |
  | `prompt`           | always              | What to generate.                                                                                          |
  | `keyframes`        | for `i2v`           | One image starts the clip, two pin start and end, `[seconds, image]` pairs pin exact times. URL or base64. |
  | `start_video`      | for `v2v`           | The clip to continue from, mp4 as URL or base64.                                                           |
  | `draft_cache`      | for `draft_enhance` | The bundle from a prior draft; reproduces that generation at full quality.                                 |
  | `resolution`       |                     | `hd` (default) or `fhd`.                                                                                   |
  | `duration`         |                     | Whole seconds, 5 to 20, or `auto` (default).                                                               |
  | `aspect_ratio`     |                     | `auto` (default), `21:9`, `2:1`, `16:9`, `4:3`, `1:1`, `3:4`, or `9:16`.                                   |
  | `generate_audio`   |                     | On by default. Set `false` for a silent clip.                                                              |
  | `safety_tolerance` |                     | 0 (strictest) to 4, default 2.                                                                             |
  | `draft`            |                     | Set `true` for a fast hd preview; the result includes a `draft_cache` bundle.                              |
  | `version`          |                     | `latest` (default) — always tracks the current release.                                                    |

  ## Use cases

  Deeply customizable, and built for use cases, visual styles, and aspect ratios
  far beyond conventional cinematic output.

  ### Text to video

  Generate a clip from words alone — FLUX 3 handles everything from a single line to a densely directed brief, filling in framing, motion, and mood wherever you leave them open.

  <div className="flux3-usecase-slides">
    <ClipSlider
      items={[
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/fc743c980a0259403ccec6c9a705f9e412e6f81b.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/96a96727d53a1d45c9854c8ef9ef67858361871a-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/5e5035fd8ef045c42098369da9318d42af477077.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/6e6d331597c675fd859a23aee149729f3f67e21b-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/54b9d5d4b47ee20eb7650099b2e27f353eb1acc5.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/9432cfd9aa0b32e403f67897e535f4868ea66c10-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/cbb9548e1a2d9f7b8d8d22644bef6489425aad32.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/9a8414f02355d61231b7999decb2be143a746ff1-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/77b2fbf640b456ff93b2eca5bf013b7e1145ea5f.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/d0cadba3ca86e84c1e6d86de87555a20f522f62c-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/5543d62e19e4bcc3b23d356a1065ff90f15552dd.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/45474bc809f92e1b9a392d8ec209a857c4e2b24d-1280x704.png" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/8d214be331058ca6b63506dfdbe713bde0a58b77.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/ceae0e535b13ed1e6753ec2cef2555f7d8542c4d-1280x704.png" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/21143c68277202cd96809af03e2c536b9424c8e9.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/2279faa8dd903dd3ef91f16913fde8f616aafd42-1440x608.png" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/40e995b48d5af914a99f053d207b91cbf0bd516b.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/4723e8a3bab8fcc8bae9c2e27e164c08e2115e72-1280x704.png" },
]}
    />
  </div>

  <Accordion title="Example API call">
    ```bash cURL theme={null}
    curl -s -X POST https://api.bfl.ai/v1/flux-3-video \
      -H "x-key: $BFL_API_KEY" -H "Content-Type: application/json" \
      -d '{
        "mode": "t2v",
        "prompt": "a fox running through dawn mist",
        "generate_audio": true
      }'
    ```
  </Accordion>

  ### Image to video

  Animate from images you pin. A single still becomes the exact opening frame, a start-and-end pair interpolates the motion between them, and several frames spread across the clip.

  <div className="flux3-usecase-slides">
    <ClipSlider
      items={[
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/3bf3b94bfd068396e9b7b7c73421c5316fbc3048.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/b015fdbe652ebfac7e834067fe9091d670f35cff-1280x736.png" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/9b7469e7f349b4f26fe9809b9faa13567504aa84.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/a121750cb4422945a6d3ff0eed246271750e3c63-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/ce0f3c021525c568ad591daf681f3d9c601f21aa.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/58430b2254241f059ce59ec01ed4b11ad9fce95e-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/b993a73e27a11f6ba0e64f25616cb0786685085c.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/2a418d0b787e96624ade85707bff5100e9742838-1280x736.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/6bc0f335c3638949f9df86ca1d3c8b273eac8e25.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/a9427d24b941e1cf9494b18387b06f2a5439669d-1280x704.jpg" },
]}
    />
  </div>

  <Accordion title="Example API call">
    ```bash cURL theme={null}
    curl -s -X POST https://api.bfl.ai/v1/flux-3-video \
      -H "x-key: $BFL_API_KEY" -H "Content-Type: application/json" \
      -d '{
        "mode": "i2v",
        "prompt": "push forward through the trees",
        "keyframes": "https://example.com/still.jpg"
      }'
    ```
  </Accordion>

  ### Multiple scenes

  Block several shots and camera angles in one generation. Character, look, and continuity hold across hard cuts, with a single audio bed carrying through.

  <div className="flux3-usecase-slides">
    <ClipSlider
      items={[
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/24be70f578f03010f5fe5426e57c65a6e5921706.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/f4227eacc451c012bea71ff4a4c73dbd2b74d6b1-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/b99a94ed131a2dcc95066a3d347014d7eea2d063.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/1a5705842db80e336cc914f640515a02e735f5cb-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/e275b9bb21f8c0b09805ee8e008ef8c06aa7162b.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/631fc883f4b5a29dc8e9cb7197f5525ffd8043a6-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/82ae19ea0ca3a328a490f5514417f6724bfbf416.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/17f424b1fd8fd93d6a09d24c53aa0f5d7afce7c7-1280x704.jpg" },
]}
    />
  </div>

  <Accordion title="Example API call">
    ```bash cURL theme={null}
    curl -s -X POST https://api.bfl.ai/v1/flux-3-video \
      -H "x-key: $BFL_API_KEY" -H "Content-Type: application/json" \
      -d '{
        "mode": "t2v",
        "prompt": "SHOT ONE: wide aerial of a desert highway at dawn. HARD CUT. SHOT TWO: interior close-up of the driver. HARD CUT. SHOT THREE: the car shrinks into the heat haze. One music bed across all shots.",
        "duration": 10
      }'
    ```
  </Accordion>

  ### Multilingual dialogue

  On-camera speech in many languages, with accurate accents and tight lipsync. Quote the line in your prompt and the character says it.

  <div className="flux3-usecase-slides">
    <ClipSlider
      items={[
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/10b82418b5cb41003dfed28f36b000f5d2a859d4.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/f7b40cc69d9264e534aababdcdf789608de9552b-1280x704.png" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/87193f21069e572ba8082488ae8123b9e9c0ddbd.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/7baa8fdf0b8280a8ee7a9ffe80283e69f52387aa-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/27c782504418881e54d7008e6ad8f1ec73590b8c.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/0fc6da2a8639ed60bb706398434c1fdd12d658ad-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/ad9f876b4a385c7be2d7639b63933f30e64a25f0.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/a3dc6c56a03ed9a26964734515a1d8d19f07f8f2-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/e9bc8a59903d4912825410441bb3c16a23d07e9f.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/a35a1bff78edb3e6ab18acf68c9c5b9da8eba04a-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/ad885036a38d8f1c47008719a13322a52ca68299.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/2f1cd533fdd713d52cba8197fbf6f47be50184a2-1280x704.png" },
]}
    />
  </div>

  <Accordion title="Example API call">
    ```bash cURL theme={null}
    curl -s -X POST https://api.bfl.ai/v1/flux-3-video \
      -H "x-key: $BFL_API_KEY" -H "Content-Type: application/json" \
      -d '{
        "mode": "t2v",
        "prompt": "A presenter speaks to the lens: \"Storm season is here.\" Clean studio lighting.",
        "generate_audio": true
      }'
    ```
  </Accordion>

  ### Broad range of styles

  Photoreal cinematography, animation, motion design, and stylized looks — the same model spans styles far beyond conventional footage.

  <div className="flux3-usecase-slides">
    <ClipSlider
      items={[
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/8f211de4af11cc268406edf8c61d07a88487c5cb.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/6e063f229f1282549f209fe7f248ec6f0fe037e6-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/aaf25c1cee9bec53385a281eecd983e60efad549.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/d79995d2a2db1c4843d32bcf6084a85fe1ea641f-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/5bcf3feb0b6b8041bd9666648d3f234dc862133c.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/df4a7b56a41e9dba0b96c6a04ddcbbc969ad2855-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/13bd9565c5cc73d8d447ca5c531d69fe6a14dc87.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/26b46a1f8e6e529aaa00060bf0c0163d8f77e55f-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/5e8a2414bbd8a1047b5f12866007851f226692e9.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/0d72a611f4feda1a2b859600eeb251fdea953a58-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/5729a757e0c75e447e07e8d9d81890ee1497e60d.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/0cd21bacc2c6bbfccab274ec0c01a1d3c583c899-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/b2d51a02a819316f46e988b2e2f4a5cb22425dd8.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/409aeb06da187764f5e7d57bacd2a606a0266cde-1280x704.png" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/3471a6e0c6bdcf5d8cd27d76eee5412d832ab373.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/82648cc4565f160cfce5c40eb3af7d055648f584-1280x704.png" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/95a79d0f1046a3b74aecd78c94ccdcba8d5bdfc5.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/fe6d57fb1ddf8f82d96d32fe7184bb6074dc98b6-1280x704.png" },
]}
    />
  </div>

  <Accordion title="Example API call">
    ```bash cURL theme={null}
    curl -s -X POST https://api.bfl.ai/v1/flux-3-video \
      -H "x-key: $BFL_API_KEY" -H "Content-Type: application/json" \
      -d '{
        "mode": "t2v",
        "prompt": "2D hand-drawn animation of a fox leaping through a paper-cut forest, bold flat colors",
        "aspect_ratio": "16:9"
      }'
    ```
  </Accordion>

  ### Text and typography

  Legible, well-placed text rendered as part of the scene — titles, signage, and lower-thirds that stay stable through motion.

  <div className="flux3-usecase-slides">
    <ClipSlider
      items={[
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/123e67046dd3f79f72f548c0b28032298935861f.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/8cb4ad88bf80f0a94d5150019fb8e1dc12adba6a-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/d4cf506cb619726f87edc91b80d59cfbf35c6c24.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/d74a6cbe510075724fec3449d173eb0250c75d4a-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/25419bd396d4efabca1295c6dbf6dedd27372022.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/fc5777f2e9b8bb14a975ac8c888a740cf7e4317f-1280x704.jpg" },
{ video: "https://cdn.sanity.io/files/2gpum2i6/production/f022402b3fb1e9105edfb0cffb44512e9495aa9b.mp4", poster: "https://cdn.sanity.io/images/2gpum2i6/production/50a2377cbb5d5f7014cd6e1f1033955e01b87285-1280x704.jpg" },
]}
    />
  </div>

  <Accordion title="Example API call">
    ```bash cURL theme={null}
    curl -s -X POST https://api.bfl.ai/v1/flux-3-video \
      -H "x-key: $BFL_API_KEY" -H "Content-Type: application/json" \
      -d '{
        "mode": "t2v",
        "prompt": "Bold kinetic title card, the word \"FLUX\" assembling from light streaks on a dark stage",
        "aspect_ratio": "16:9"
      }'
    ```
  </Accordion>

  ### Keyframes

  Pin ordered keyframes and FLUX 3 interpolates one continuous shot through each — hit this pose, then this composition, then this — while the model handles the motion between them.

  <Tabs>
    <Tab title="Example 1">
      <Columns cols={4}>
        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/809c8dc93242314493c4ae613e9ebfba26b7b14f-2752x1536.jpg" alt="Keyframe — Start · 0:00" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Start · 0:00</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/716f9cad904a337d7d54cbe3fa69d8cc8fb6fde5-2752x1536.jpg" alt="Keyframe — Key 2 · 0:03" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Key 2 · 0:03</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/b4855f2bcf674551255248f4f30a2f213ed34e68-2752x1536.jpg" alt="Keyframe — Key 3 · 0:07" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Key 3 · 0:07</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/b591a58d3a4a94472dbb7e69f3f40859cc6d3731-2752x1536.jpg" alt="Keyframe — End · 0:10" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>End · 0:10</p>
        </div>
      </Columns>

      <MutedVideo src="https://cdn.sanity.io/files/2gpum2i6/production/afc86a790f1a835db3cff62e58dd658c3d47fc59.mp4" alt="Keyframes interpolation — example 1" />
    </Tab>

    <Tab title="Example 2">
      <Columns cols={4}>
        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/358bebb751c3867f0901e30124f3883e76ab7bbb-2752x1536.jpg" alt="Keyframe — Start" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Start</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/54cea0c9d4713e182e6efca23af4aaded1692eeb-1280x704.jpg" alt="Keyframe — Key 2" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Key 2</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/7bae0d042e4439dc663ef4c9a9e742a0ea7d7ba2-2752x1536.jpg" alt="Keyframe — Key 3" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Key 3</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/5e23500b2d474706664f50d049b773881bdf08df-2752x1536.jpg" alt="Keyframe — End" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>End</p>
        </div>
      </Columns>

      <MutedVideo src="https://cdn.sanity.io/files/2gpum2i6/production/3f5235ad7fc4479887c52b832e0b53162443192a.mp4" alt="Keyframes interpolation — example 2" />
    </Tab>

    <Tab title="Example 3">
      <Columns cols={4}>
        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/8ceb96c14c79c734374de4eca3ee56ba6c122cb9-2752x1536.jpg" alt="Keyframe — Start · 0:00" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Start · 0:00</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/46eba9ae12d22d874b38bbdb4912d8b5ce0ae174-2752x1536.jpg" alt="Keyframe — Key 2 · 0:03" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Key 2 · 0:03</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/e3dfbe33f753a1ce8b3cae684dd4b6a623a41508-2752x1536.jpg" alt="Keyframe — Key 3 · 0:07" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Key 3 · 0:07</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/49e52bc3ba7be3391053bd0a44b65312955acbc6-2752x1536.jpg" alt="Keyframe — End · 0:10" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>End · 0:10</p>
        </div>
      </Columns>

      <MutedVideo src="https://cdn.sanity.io/files/2gpum2i6/production/44b47fe10cbc6e12254a62e551039f6fdbe0344c.mp4" alt="Keyframes interpolation — example 3" />
    </Tab>

    <Tab title="Example 4">
      <Columns cols={4}>
        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/a0250de4e82156784a473cbc3d80962eec182f67-2752x1536.jpg" alt="Keyframe — Start · 0:00" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Start · 0:00</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/0fb824a0f323555e7bf638ba15297560929f5910-2752x1536.jpg" alt="Keyframe — Key 2 · 0:03" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Key 2 · 0:03</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/b53d1ad12728e294e24ac379f9911fa6d23394df-2752x1536.jpg" alt="Keyframe — Key 3 · 0:07" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>Key 3 · 0:07</p>
        </div>

        <div>
          <img src="https://cdn.sanity.io/images/2gpum2i6/production/5f041547138189029876ea4e578ab1f563fad5f4-2752x1536.jpg" alt="Keyframe — End · 0:10" style={{ width: "100%", display: "block", borderRadius: "0.5rem" }} />

          <p style={{ textAlign: "center", marginTop: "0.4rem", opacity: 0.7, fontSize: "0.85rem" }}>End · 0:10</p>
        </div>
      </Columns>

      <MutedVideo src="https://cdn.sanity.io/files/2gpum2i6/production/f5b698d51ec802449f9facb5d57ab62b5dd1c68c.mp4" alt="Keyframes interpolation — example 4" />
    </Tab>
  </Tabs>

  <Accordion title="Example API call">
    ```bash cURL theme={null}
    curl -s -X POST https://api.bfl.ai/v1/flux-3-video \
      -H "x-key: $BFL_API_KEY" -H "Content-Type: application/json" \
      -d '{
        "mode": "i2v",
        "prompt": "a seed grows into a tree through the seasons",
        "duration": 10,
        "keyframes": [[0, "https://example.com/seed.png"], [4.5, "https://example.com/sapling.png"], [10, "https://example.com/tree.png"]]
      }'
    ```
  </Accordion>

  ### Video continuation

  Feed an existing clip and FLUX 3 picks up from its final frames, carrying momentum, framing, and scene logic forward without a cut.

  <Tabs>
    <Tab title="Elephants">
      <VideoExtension src="https://cdn.sanity.io/files/2gpum2i6/production/08c30d095b03c80ebec84e8a547c3ea694c2efca.mp4" poster="https://cdn.sanity.io/images/2gpum2i6/production/2b51df2fc7e43550f57b1cb72fe64b6122197de7-1280x704.jpg" originalDuration={5} alt="Elephants — video continuation" />
    </Tab>

    <Tab title="Penguin">
      <VideoExtension src="https://cdn.sanity.io/files/2gpum2i6/production/c3251e7b67573784027544e16752c517eb73f7b7.mp4" poster="https://cdn.sanity.io/images/2gpum2i6/production/43a63bebd9601c5748797cd2aa6e71785dbf8b23-1280x704.jpg" originalDuration={5} alt="Penguin — video continuation" />
    </Tab>

    <Tab title="Iguazú Falls">
      <VideoExtension src="https://cdn.sanity.io/files/2gpum2i6/production/69c7cd8f26d64630880b5e330581cb2312f0645c.mp4" poster="https://cdn.sanity.io/images/2gpum2i6/production/ab596e488b835529ccb40b0d3ba26be1ae8b9dd6-1280x704.jpg" originalDuration={5} alt="Iguazú Falls — video continuation" />
    </Tab>

    <Tab title="Giraffe">
      <VideoExtension src="https://cdn.sanity.io/files/2gpum2i6/production/d1368e9ad2193e8e85b0165b6fa793e4920546f1.mp4" poster="https://cdn.sanity.io/images/2gpum2i6/production/42c4ae3bab1aca4a3f86e387526daf501c635d64-1280x704.jpg" originalDuration={5} alt="Giraffe — video continuation" />
    </Tab>

    <Tab title="SUV drift">
      <VideoExtension src="https://cdn.sanity.io/files/2gpum2i6/production/8f587923157cc33e6558fdd4d6c0a96ff6a6fdc8.mp4" poster="https://cdn.sanity.io/images/2gpum2i6/production/a1de57d82c1f75869133b4557b81faf3a935f75c-1280x704.jpg" originalDuration={5} alt="SUV drift — video continuation" />
    </Tab>
  </Tabs>

  <Accordion title="Example API call">
    ```bash cURL theme={null}
    curl -s -X POST https://api.bfl.ai/v1/flux-3-video \
      -H "x-key: $BFL_API_KEY" -H "Content-Type: application/json" \
      -d '{
        "mode": "v2v",
        "prompt": "she takes his hand and pulls him laughing through the lantern-lit alley, the camera chasing them",
        "start_video": "https://example.com/clip.mp4",
        "duration": 10
      }'
    ```
  </Accordion>

  ## Keep exploring

  <CardGroup cols={2}>
    <Card title="Prompting Guide" icon="pen" href="/guides/prompting_video_text_to_video">
      Prompt formats, the schema, camera language, and audio.
    </Card>

    <Card title="API reference" icon="code" href="/api_reference/flux3">
      The full flux-3-video request contract: modes, fields, and constraints.
    </Card>

    <Card title="Playground" icon="play" href="https://playground.bfl.ai">
      Try FLUX 3 in your browser — no setup required.
    </Card>

    <Card title="FLUX 3 overview" icon="film" href="/flux_3/flux3_overview">
      One request shape across image, video, and synchronized audio.
    </Card>
  </CardGroup>
</div>
