Documentation: all sections

Video

Saxeo Video renders text-to-video and image-to-video jobs through the same key, the same balance, the same budgets, and the same signed receipts as every other billable call.

It is asynchronous, and that is the one thing to internalise before writing any code against it. A render takes roughly 30 seconds to 5 minutes. Unlike image generation, which hands you the picture on the response, video returns a job id immediately and you poll for the result.

Availability

Video is off by default and config-gated. It is live only on a deployment where an operator has set SAXEO_VIDEO_PROVIDERS and configured that provider's credentials. No video provider is enabled on the production deployment today. Where it is not enabled, the model list comes back empty and a generation call returns a configuration error rather than a video.

GET /v1/videos/models is public and is the authoritative check for what, if anything, is enabled here. An empty data array means video is not available on this deployment.

# Public: what video models are enabled here (empty when off)
curl https://www.saxeonetwork.tech/__api/v1/videos/models
{
  "object": "list",
  "data": [
    {
      "id": "sable-video",
      "kind": "video",
      "mode": "text-to-video",
      "price_usd_per_second": 0.4,
      "max_duration_secs": 10,
      "default_duration_secs": 5,
      "default_resolution": "1080p",
      "durations": [3, 5, 8, 10],
      "resolutions": ["720p", "1080p"],
      "aspect_ratios": ["16:9", "9:16", "1:1"]
    }
  ]
}

The job lifecycle

1CallPOST /v1/videos/generationsWhat happensThe render is handed to the provider synchronously, so a bad request fails immediately. Returns 202 with a job id.
2CallGET /v1/videos/generations/:idWhat happensPoll. status and progress advance.
3CallGET /v1/videos/generations/:id/contentWhat happensOnce status is succeeded, download the bytes.
CallPOST /v1/videos/generations/:id/cancelWhat happensBest-effort stop.
CallDELETE /v1/videos/generations/:idWhat happensDestroy the stored video now rather than at its TTL.

Status vocabulary

Fixed and exhaustive — you may switch on it:

queuedMeaningAccepted; the provider has not started.Billed?No
runningMeaningRendering. progress is whole percent, 0100.Billed?No
succeededMeaningDone. asset_available is true and /content serves the bytes.Billed?Yes
failedMeaningThe render failed, or the job passed its deadline. error_class says which.Billed?No
canceledMeaningYou cancelled it before the provider produced anything.Billed?No
expiredMeaningIt succeeded, and the stored video has since passed its TTL and been destroyed.Billed?Yes (at the time)

The spelling is succeeded — not ok, not complete.

Generating

curl -X POST https://www.saxeonetwork.tech/__api/v1/videos/generations \
-H "authorization: Bearer $SAXEO_API_KEY" \
-H 'content-type: application/json' \
-d '{
  "model": "sable-video",
  "prompt": "a slow pan over a black lake at dusk",
  "duration_secs": 5,
  "aspect_ratio": "16:9",
  "resolution": "1080p"
}'

Request fields

modelNotesRequired. A saxeo id from GET /v1/videos/models.
promptNotesRequired. Up to 4,000 characters.
duration_secsNotesSeconds of output. Rounded up to whole seconds and clamped to the model's maximum. Omitted ⇒ the model default.
aspect_ratio, resolution, seedNotesOptional; passed through to the model.
imageNotesBase64 or a data: URL. Required on an image-to-video model, and refused on a text-to-video one — silently ignoring it would bill you for a video that had nothing to do with your image.
nNotesMust be 1. Price is per second, so two jobs cost exactly what n: 2 would.
sable_run_idNotesChains this job's receipt into an agent run.

Pricing: per second, not per job

Video is priced per second of produced output, because duration is yours to choose. price_usd_per_second on the model listing is the rate.

Two consequences worth knowing before your first bill:

A job that fails, times out, or is cancelled before the provider produced anything bills nothing and releases its reservation.

Cancelling

POST /v1/videos/generations/:id/cancel polls the provider once before deciding, because the honest answer depends on what actually happened:

Read billed; do not assume.

Storage, the TTL, and §3

Saxeo's privacy contract says prompts, completions and submitted code are never persisted. Video needs one disclosed exception, and here it is plainly:

Some backends return a URL rather than bytes. Saxeo fetches it server-side into the sealed store and never hands you the provider's link. Passing it through would leak your request to a third party outside Saxeo's contract, and it would expire on that provider's schedule instead of the TTL published here.

The receipt

Every settled job mints a signed, metadata-only receipt, stored like every other and verifiable at the public POST /v1/receipts/verify.

{
  "v": 1,
  "kind": "video",
  "request_id": "vid_…",
  "model": "sable-video",
  "engine": "…",
  "provider": "video:…",
  "unit": "video_seconds",
  "quantity": 5,
  "duration_secs": 5,
  "resolution": "1080p",
  "content_fingerprint": "…",
  "output_sha256": "…",
  "cost_micro_usd": 2000000,
  "latency_ms": 41230,
  "created_at": "…"
}

output_sha256 is the hash of the exact bytes produced, so a receipt and the file can be checked against each other years later.

Each video also carries a signed provenance manifest, the same content-free shape images use, verifiable at POST /v1/videos/verify (an alias for the image verifier — one verifier, one answer). It reports "embedded": false for video: MP4 and WebM are recognised but the manifest is deliberately not written into the container, because a half-correct box produces files some players reject. The manifest travels beside the file instead, and Saxeo never claims an embedding it did not do.

From an agent

The routes are key-authed, so a hosted agent's injected key reaches them with no extra plumbing. Over MCP the tools are sable_generate_video (returns the job id immediately — an MCP call must never block for five minutes) and sable_video_status (poll it).

Limits and failure classes

In-flight jobs per account4 by default; over it returns 429 with Retry-After. One caller cannot monopolize the poller.
Per-job deadline15 minutes by default. Past it the job is failed with error_class: "timed_out" and nothing is billed.
Asset ceiling64 MiB by default, enforced while downloading.

error_class is always a fixed identifier — provider_failed, no_output, timed_out, download_failed, job_not_found, provider_unconfigured — never provider prose, which can echo your prompt back at you.