Video Generation

The video generation endpoints support async-style creation via POST /v1/videos and status polling via GET /v1/videos/{id}, with deterministic responses driven entirely by your fixture.

Endpoints

Method Path Format
POST /v1/videos JSON or multipart form-data (create video job)
GET /v1/videos/{id} JSON (poll status)

OpenRouter's dedicated video-generation surface (/api/v1/videos) is documented separately at OpenRouter Video.

Async Polling Pattern

Real video generation is asynchronous: the POST endpoint returns a job ID, and the GET endpoint returns the current status. aimock does not simulate status progression on this surface — the create response and every subsequent poll echo the fixture's video.status verbatim. A fixture with "status": "completed" is completed immediately; a fixture with "status": "processing" stays processing on every poll. If you need staged progression (pending → in_progress → completed), use the OpenRouter Video surface with its openRouterVideo polling configuration.

Unit Test: Create and Poll

Using the programmatic API with vitest, register a fixture and test the full async flow.

video-polling.test.ts ts
import { LLMock } from "@copilotkit/aimock";
import { describe, it, expect, beforeAll, afterAll } from "vitest";

let mock: LLMock;

beforeAll(async () => {
  mock = new LLMock();
  await mock.start();
});

afterAll(async () => {
  await mock.stop();
});

it("creates a video job and polls its status", async () => {
  mock.onVideo("a cat playing piano", {
    video: {
      id: "video_123",
      status: "completed",
      url: "https://example.com/cat-piano.mp4",
    },
  });

  // Step 1: Create the video job
  const createRes = await fetch(`${mock.url}/v1/videos`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      model: "sora-2",
      prompt: "a cat playing piano",
    }),
  });

  const createBody = await createRes.json();
  expect(createBody.id).toBe("video_123");
  expect(createBody.status).toBe("completed");
  expect(createBody.url).toBe("https://example.com/cat-piano.mp4");
  expect(createBody.created_at).toBeDefined();

  // Step 2: Poll the job — the status echoes the fixture
  const pollRes = await fetch(`${mock.url}/v1/videos/${createBody.id}`);
  const pollBody = await pollRes.json();

  // The poll body is flat: { id, status, created_at, url }
  expect(pollBody.status).toBe("completed");
  expect(pollBody.url).toBe("https://example.com/cat-piano.mp4");
});

JSON Fixture

fixtures/video.json json
{
  "fixtures": [
    {
      "match": { "userMessage": "cat playing piano", "endpoint": "video" },
      "response": {
        "video": {
          "id": "video_123",
          "status": "completed",
          "url": "https://example.com/cat-piano.mp4"
        }
      }
    }
  ]
}

Response Format

Create (POST /v1/videos)

Poll (GET /v1/videos/{id})

The poll response is flat — there is no nested video object.

Video fixtures use match.userMessage which maps to the prompt field in the creation request. The fixture's video.id and video.status are required — aimock echoes them in the create response and on every poll, without progression.

Record & Replay

When no fixture matches an incoming request, aimock can proxy it to the real API and record the response as a fixture for future replays. Enable recording with the --record flag or via RecordConfig in the programmatic API. Completed videos are recorded with their final URL; in-progress upstream responses are saved with "status": "processing" and replay with that status verbatim, without hitting the real API.

CLI sh
npx -p @copilotkit/aimock llmock --record --provider-openai https://api.openai.com