Async jobs (video)
Video generation is asynchronous: you create a job, then poll until it
reaches a terminal status. The gateway runs this lifecycle through the standard
POST /v1/videos door, adding tenant safety and accurate billing on top.
The kinds that go through this create-then-poll lifecycle and appear in
GET /gw/async are:
video— via the standardPOST /v1/videosdoor (this page) or a provider’s native prefix.image— some image models are async upstream too; thePOST /v1/imagesdoor (create) /GET /v1/images/{image_id}(poll) mirrors the video lifecycle with a202on create — see media doors.3d— the same async machinery supports it, but no3dmodels are in the live catalog yet.audio— some ASR models run as async jobs, but only over a provider’s native prefix (e.g./alibaba); there is no/v1/audio/*async door — see transcription (ASR) for the native-prefix lifecycle.
Embeddings, reranks and TTS (audio/speech) all return synchronously —
one request, one response — and never create an async job, so they do not
appear in GET /gw/async; their usage shows up only in
GET /gw/usage. See
embeddings, reranks & speech for those
synchronous doors. The POST /v1/images/generations OpenAI-Images door is also
always synchronous — see media doors for the split between
that and the async POST /v1/images door.
Lifecycle
Section titled “Lifecycle”You address a video model by its catalog slug author/model; the door resolves
the slug and routes to wherever inference runs. The POST /v1/videos door is
live today and serves both Alibaba Wan and ByteDance Seedance.
CREATE POST /v1/videos → returns a job idPOLL GET /v1/videos/{id} → status: pending | succeeded | failed | ...LIST GET /gw/async → served from the gateway ledgerExample (ByteDance Seedance):
# CREATEcurl https://api.sociaro.com/v1/videos \ -H "Authorization: Bearer gw_live_..." \ -H "Content-Type: application/json" \ -d '{"model": "bytedance/seedance-1-5-pro", "prompt": "A cat playing piano", "seconds": 5}'# → {"id": "video_...", "status": "pending", ...}
# POLL until terminalcurl https://api.sociaro.com/v1/videos/video_... \ -H "Authorization: Bearer gw_live_..."The door returns a single, consistent job shape regardless of which model serves
the request: a video_... job id at create, then a status field that moves to
a terminal state. You always read the job id from the create response’s id
field and poll the same door — no provider-specific field names to track.
The Python SDK drives this loop for you
(client.video.generate(...) / client.video.submit(...)).
The catalog covers current video/image generations — Wan 2.7, Seedance 2.0,
Seedream 5.0 and more (see GET /v1/models). The door translates
only the envelope (model, prompt, seconds, size) and passes any other
top-level field (e.g. seed, ratio, watermark, negative_prompt) straight
through to the provider’s native body, so provider-specific parameters work
without leaving the door — see media doors.
Native vendor prefix (alternative)
Section titled “Native vendor prefix (alternative)”For genuine vendor APIs you can also call the native transparent-proxy prefix
directly — e.g. /alibaba (Alibaba DashScope). The gateway forwards the request
to the vendor and records the job in the same ledger, with the same tenant
isolation and billing. Most callers should prefer the standard POST /v1/videos
door, which gives one consistent job shape across every video model.
What the gateway adds
Section titled “What the gateway adds”- Tenant isolation. Every job is recorded in the gateway’s ledger.
Polling a task that belongs to another organization returns
404— the request never reaches the provider. The gateway serves LIST from its own ledger (GET /gw/async) rather than proxying any provider-side list (which would leak other tenants’ jobs). - Accurate billing. At CREATE time the job is metered with a provisional cost (from your request’s duration/resolution parameters). When the job reaches a terminal state — via your poll or the gateway’s background poller — the cost is finalized exactly once: succeeded jobs get the real final cost, failed/cancelled jobs are zeroed. See Final-cost precedence below for how the final number is derived.
- Background finalization (reaper). If you stop polling, the gateway
polls the provider itself and finalizes the job, so your analytics never
show permanently “pending” spend. Jobs that can no longer be finalized are
marked
expired(e.g. after the polling TTL, or when upstream credentials were rotated mid-flight).
Final-cost precedence
Section titled “Final-cost precedence”When a job succeeds, the gateway computes final_cost_usd from the terminal
provider response, not from your original request. It tries the following
sources in order and stops at the first one that applies (the source it used is
recorded as finalize_source on the ledger row):
explicit_cost— the provider reported a final cost in USD in its terminal task body. That figure is used verbatim, overriding all tariff math.explicit_credits— the provider reported a credit count, and the catalog tariff defines acredit_to_usdconversion. The cost iscredits × credit_to_usd. (Aper_credittariff is billable only this way — without a reported credit count it falls back to the provisional.)computed— neither explicit figure is present, so the cost is derived from the tariff and the actual quantities the provider reports in the terminal task (tokens, seconds of video/audio, resolution, image count, megapixels, or aflatper-job rate), not your requested values.provisional_fallback— none of the above is computable (a tariff miss, e.g. the provider didn’t return the metering field). The provisional charge from CREATE stands as the final cost; nothing is silently set to zero.
Explicit cost and explicit credits are absolute amounts from the provider, so
they are used as-is. The token/per-second/per-image/per-megapixel rates can be
scaled by a variant multiplier: for a few models the rate depends on a
single CREATE-time parameter (e.g. Seedance’s generate_audio — generating
without audio bills at half rate). The catalog base rate is the worst case;
a recognised variant value only discounts it. If you omit the parameter, or
send a value the catalog doesn’t know, you are billed the full worst-case rate
(never an under-charge). The variant parameter is read from your CREATE request
and applied to both the provisional and the final cost. Variant multipliers do
not apply to explicit_cost, explicit_credits, or flat tariffs.
The authoritative per-job figure is always the final_cost_usd on the
GET /gw/async row (or provisional_cost_usd
while the job is still pending).
Job statuses
Section titled “Job statuses”| Status | Meaning | Billing |
|---|---|---|
pending | Created, not yet terminal | provisional cost |
succeeded | Completed | final cost (replaces provisional) |
failed | Provider reported failure | cost zeroed |
cancelled | Cancelled | cost zeroed |
expired | Gateway could not finalize (expiration_reason: ttl or credential_rotated) | provisional cost stands |
Inspecting your jobs
Section titled “Inspecting your jobs”Use GET /gw/async to list jobs with status,
provisional/final cost and attribution — filterable by status, kind and time
range, with cursor pagination. The same data backs the dashboard’s
Async Jobs page.
In raw usage rows (GET /gw/usage), async
requests appear with parse_status = provisional until finalization, then
ok (succeeded), failed, cancelled, or expired.