Sell video
This page is for a provider that renders video. Use the reference agent if your upstream has the three OpenAI video routes below. Then register the deployment and test it.
Use an agent
A video job needs an agent. The OpenAI-compatible path does not carry video jobs, and the exchange sends them to the next offer.
A render takes minutes, and the buyer downloads the file later. The agent does three jobs that a plain API cannot do:
- It starts the render and waits for it to end.
- It reports the seconds that you rendered, so that the job settles.
- It names the file to the exchange and serves the file when the buyer downloads it.
Choose one of two agents:
| Choice | Use it when |
|---|---|
| The reference provider agent | Your upstream has the OpenAI video routes that this page describes. You write no code. |
| Your own agent | Your upstream has other routes. Follow the Video section of the dispatch page and send video_ready yourself. |
The rest of this page is for the reference agent.
Give the agent these routes
Set the base URL of your upstream in LIQUID_UPSTREAM_BASE_URL. The agent adds the paths below to it and sends your key as Authorization: Bearer <key>.
| The agent calls | Your answer |
|---|---|
POST {base}/videos |
A JSON job with an id. |
GET {base}/videos/{id} |
The job with its status. |
GET {base}/videos/{id}/content |
The file, with a Content-Type that is video/*. |
Create a job
The agent sends only the fields in this example.
{
"model": "acme-video-1",
"prompt": "A trading floor made of water",
"seconds": "8",
"size": "1280x720",
"input_reference": { "image_url": "https://example.com/start.png" }
}
| Field | Value |
|---|---|
model |
The upstream model of the deployment. |
seconds |
A string, as in the OpenAI API. |
size |
Present only when the buyer asked for a size. |
input_reference |
Present only when the buyer gave a start image. See the rules below. |
The agent sends at most one start image. It accepts an image with no frame type or with the frame first_frame. It fails the job for the frame last_frame.
Answer 2xx with a JSON body that has an id. A status that is not 2xx, or a body with no id, ends the attempt.
Report the status
The agent asks for the job at a fixed interval. The status must be one of these values:
status |
Meaning |
|---|---|
queued |
The job waits. The agent asks again. |
in_progress |
The job renders. The agent asks again. |
completed |
The file is ready. |
failed |
The job failed. Put the reason in error.message. |
Any other value fails the job.
- A
5xxanswer or a network error on a poll is tried again a few times. Then the job fails. - A
4xxanswer fails the job at once.
When the job is completed, you can add expires_at, in Unix seconds. It is the time when you delete the file.
Serve the file
The agent reads the headers of the file before it tells the exchange that the video exists. Then the buyer is charged. Make sure that GET {base}/videos/{id}/content works when the status is completed.
- Send a
Content-Typethat starts withvideo/. Any other type, or an empty file, fails the job. - Keep the file until
expires_at. The buyer can download it after the job ends.
Bill fewer seconds
The agent charges the seconds that the buyer asked for. To charge fewer, add this header to the POST answer:
x-new-api-other-ratios: {"seconds": 6}
The agent rounds the value up. It never charges more seconds than the buyer asked for, because the exchange does not pay for them.
Frames and other references
The OpenAI video routes take one start image. The reference agent does not send a last frame, audio or a video reference on this route.
The exchange sends your deployment only the frames that you declared in supported_frame_images. Declare none that the route cannot read.
To read more, write your own agent. A part of the dispatch has frame_type when it is a frame image, and you map it to your upstream fields. See Frame images.
Declare the controls
A buyer's seed, creativity, upscale_factor and previous_job_id reach only an offer that declared video.seed, video.creativity, video.upscale_factor or video.continue. The first-party OpenAI adapter declares video.seed when the route has a seed rule. It declares no other video control. To sell the others, write your own agent. See Quote a media model.
Register the deployment
Register the deployment with kind set to video_generation. Make deployment_name and model equal, because the agent asks the upstream for that name. The lengths and sizes you list are the only ones that can win.
curl https://router.inference.ai.exchange/v1/provider/models \
-H "Authorization: Bearer $LIQUID_PROVIDER_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"deployment_name":"acme-video-1",
"model":"acme-video-1",
"kind":"video_generation",
"precision":"provider-managed","region":"us",
"max_input_tokens":2000,"max_output_tokens":1,
"output_modalities":[{"type":"video","seconds":[4,8],
"sizes":["1280x720"],"formats":["video/mp4"]}]}'
Register deployments lists every field.
Quote an output_video line in pricing for the deployment. The charge is that rate multiplied by the seconds that the buyer asked for. The exchange knows the charge before dispatch, so the price cap is exactly the charge. Quote a media model has an example with variants.
If your upstream lets the buyer choose the sound or the resolution, state audio and resolutions in the video output and quote output_video lines with a variant for the options that change the price. The buyer's choice is in audio and resolution of the video object of the dispatch. See Sound, resolution and price.
Continue a video
A buyer can send previous_job_id to continue an earlier video. Declare video.continue only if your upstream can do this.
- The dispatch holds
video.previous_job_id. It is the job id that the exchange sent to you in the dispatch of the earlier video. - The exchange checks that the earlier job completed and belongs to the same workspace. It does not check that the earlier job ran on your deployment, and it sends no file or handle.
- Keep a map from each job id to your own handle. If you do not know the id, send a
failedevent. The job costs nothing and the exchange tries the next offer. - Each job is charged for its own seconds.
Cancel and expiry
The exchange sends no cancel message. It closes the dispatch connection in these cases:
- The buyer calls
POST /v1/videos/{id}/cancel. The exchange closes the connection within a few seconds. The job costs nothing. - The job runs past the video job limit, 10 minutes by default. The buyer sees
expired. The job costs nothing, and it counts as a failure of your deployment.
Watch for the close. When it comes, stop the work on your upstream if your upstream has a way to cancel. If it has none, you carry the cost of the render.
Start the agent
- Set
LIQUID_UPSTREAM_BASE_URLand the key of your upstream. - Set
LIQUID_CONTENT_KEYto a stable secret. The agent signs the download token with it. Keep the same value after a restart. If the value changes, the buyer cannot download a video that is already paid for. - Start the agent and wait for
listed, then quote the deployment. See Run your agent. - Keep the agent running for as long as your files stay on the upstream. The buyer downloads through the agent.
Limits and failures
- The exchange limits the whole job, by default to ten minutes. The agent gives up before that limit and reports its own reason.
- If the exchange closes the dispatch, the agent stops to wait. Your upstream keeps rendering, because no cancel route works on every gateway. You carry the cost of that render. See Cancel and expiry.
- The exchange can use the next offer only before the first output. After the agent reports a finished video, the job does not move to another provider.
- These failures end the attempt: a failed job, a wrong status, a file that is not
video/*, and a missingid. The exchange charges nothing and tries the next offer.
Test before you quote
Run these calls against your upstream. Each one must give the answer that this page describes.
curl -s $BASE/videos -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"model":"acme-video-1","prompt":"test","seconds":"4"}'
curl -s $BASE/videos/$ID -H "Authorization: Bearer $KEY"
curl -sI $BASE/videos/$ID/content -H "Authorization: Bearer $KEY"
Then buy one video from your own deployment with a small price cap, and download it. The buyer's video guide shows the calls.