MainSDK
Search⌘K
How-to

Generate a video

Start a video job with ai.video, let the host finish it, and get a labelled file back.

Planned for 0.6

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

You will start a video as a durable job, wait for it to finish, and read the file it wrote. It takes about 10 minutes and needs no provider account for the first run.

You need

  • files set up, because the finished clip is written there (set files.maxSize large enough for a clip).
  • For a real clip, a video provider key as a secret, such as secret:OPENAI_API_KEY.

1. Configure

main.yaml
ai:
  models: [openai/*]
  video: { driver: openai, max-seconds: 8 }   # openai | google | gateway
  prices:
    openai/sora-2: { video: { "1280x720": "$0.10/s" } }   # a model with no price cannot be called

2. Start a job

app/api/clip/route.ts
import { ai } from "mainsdk";

export async function startClip(user: { id: string }) {
  const job = await ai.video.generate({
    model: "openai/sora-2",
    prompt: "A paper boat on a stream",
    seconds: 4,
    feature: "promo",
    user,
    maxCost: "$1",   // refused before anything is submitted when the worst case is higher
  });
  return Response.json({ id: job.id, state: job.state });   // queued; nothing waits for the provider
}

The request returns at once. A durable workflow submits the job, waits, fetches the output into files and finishes, even across a restart.

3. Read the result

app/api/clip/[id]/route.ts
import { ai } from "mainsdk";

export async function readClip(id: string) {
  const job = await ai.video.get(id);   // queued, running, ready, failed or cancelled
  return job.state === "ready" ? Response.json({ file: job.file, cost: job.cost }) : Response.json({ state: job.state });
}

ai.video.cancel(id, { reason }) stops the job at the provider and records any cost already billed.

Check it worked

Run with driver: fake and ask for more seconds than max-seconds: the call fails with MAIN-AI-0050 and nothing is sent. A job id from another tenant fails with MAIN-AI-0051. A finished clip carries the ai-generated label, and with ai.credentials.signer set it also carries a signed content credential that ai.credentials.verify reports as verified.

Next

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet