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.
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": [
{
"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)
-
id— job identifier, taken from the fixture'svideo.id -
status— the fixture'svideo.status:"processing","completed", or"failed" created_at— Unix timestamp set when the job is created-
url— URL of the generated video; included only whenstatusis"completed"
Poll (GET /v1/videos/{id})
The poll response is flat — there is no nested video object.
id— the job identifier-
status— the fixture'svideo.status, echoed verbatim:"processing","completed", or"failed" created_at— Unix timestamp from job creation-
url— URL of the generated video; present whenever the fixture setsvideo.url
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.
npx -p @copilotkit/aimock llmock --record --provider-openai https://api.openai.com