LLM Gatewaydocs

Video

Video generation is asynchronous. You submit a job, receive 202 Accepted immediately, then either poll for the result or receive a signed webhook when the job finishes.

POST/v1/video/generations202 · Idempotency-Key REQUIRED
GET/v1/video/generations/{id}poll
DELETE/v1/video/generations/{id}cancel (non-terminal) or delete (terminal)
GET/v1/jobs/{id}kind-agnostic job view
Provider availability

The job pipeline, state machine, idempotency, polling, cancellation and webhooks are fully implemented. Real video providers are not yet wired: unless your operator has enabled the mock media provider, submissions return 501 not_implemented with a structured error. Check GET /v1/models for models with modality: "video".

1. Submit a job

bash
curl "https://api.smartapihub.com/v1/video/generations" \
  -H "Authorization: Bearer $LLM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "model": "vendor/video-model",
    "prompt": "A slow aerial shot over a foggy pine forest at sunrise",
    "duration_seconds": 8,
    "aspect_ratio": "16:9",
    "resolution": "1080p",
    "audio": false,
    "webhook_url": "https://api.example.com/hooks/llm-jobs",
    "metadata": { "order_id": "4821" }
  }'
FieldTypeNotes
modelstring, requiredA modality: "video" model.
promptstring ≤ 10,000, required
duration_secondsnumber 1–120Drives the cost estimate (per_video_second).
image_url / end_image_urlhttps URLFirst/last frame conditioning where supported. Must pass the SSRF guard.
input_imagesstring[] ≤ 10Reference images (URLs or base64).
resolutionstringe.g. 720p, 1080p — model-dependent.
aspect_ratioW:He.g. 16:9, 9:16.
audiobooleanGenerate audio track where supported.
webhook_urlhttps URLCalled on completion/failure. Public hosts only (SSRF guard).
metadataobject of stringsEchoed back in the job.
max_cost_microintegerCap the reservation. Default reservation is 1.5 × estimate.
routingobjectRouting override.

Idempotency-Key is required

Video jobs are expensive, so the header is mandatory; omitting it returns 400 idempotency_key_required. Use a UUID or a stable identifier from your own system (≤ 255 chars). Re-submitting with the same key and body returns the existing job (202, Idempotent-Replayed: true) instead of creating a duplicate. Same key with a different body → 409 idempotency_conflict.

Response — 202 Accepted

json
{
  "id": "0c1f4c2a-6b1e-4f6e-9a1e-2d7d0d8b3a11",
  "object": "video.generation",
  "status": "queued",
  "model": "vendor/video-model",
  "created_at": "2026-09-04T10:15:00Z",
  "estimated_cost_micro": 400000
}

estimated_cost_micro is reserved against your balance/budgets immediately; the reservation is settled to the actual cost when the job finishes and released if it fails, is cancelled or expires.

2. Poll for the result

bash
curl "https://api.smartapihub.com/v1/video/generations/0c1f4c2a-6b1e-4f6e-9a1e-2d7d0d8b3a11" \
  -H "Authorization: Bearer $LLM_API_KEY"
json
{
  "id": "0c1f4c2a-6b1e-4f6e-9a1e-2d7d0d8b3a11",
  "object": "video.generation",
  "request_id": "req_01J6ZK3M9PQR7S8T9V",
  "org_id": "8a4e…",
  "kind": "video",
  "model": "vendor/video-model",
  "provider": "vendor-provider",
  "status": "succeeded",
  "progress": 100,
  "estimated_cost_micro": 400000,
  "max_cost_micro": 600000,
  "cost_micro": 384000,
  "error": null,
  "params": { "prompt": "A slow aerial shot…", "duration_seconds": 8, "aspect_ratio": "16:9" },
  "artifacts": [
    {
      "id": "a3c9…",
      "type": "video",
      "content_type": "video/mp4",
      "size_bytes": 18234567,
      "width": 1920,
      "height": 1080,
      "duration_seconds": 8,
      "url": "https://media.example.com/artifacts/…/a3c9.mp4?X-Amz-Expires=3600&…",
      "expires_at": "2026-09-04T11:20:00Z"
    }
  ],
  "webhook_url": "https://api.example.com/hooks/llm-jobs",
  "created_at": "2026-09-04T10:15:00Z",
  "started_at": "2026-09-04T10:15:03Z",
  "completed_at": "2026-09-04T10:19:41Z",
  "expires_at": "2026-09-05T10:15:00Z"
}

Poll every few seconds with backoff (e.g. 2 s → 30 s). progress (0–100) is provided when the provider reports it, otherwise null. When status is failed, error holds { code, message }.

Artifact URLs are signed and short-lived (about 1 hour by default; the exact instant is in expires_at). Fetching the job again issues fresh URLs. Artifacts themselves are retained for a limited period (default 7 days) and then deleted — download what you need.

GET /v1/jobs/{id} returns the same object for any job kind (video or music) — useful when you store job ids without their kind.

3. Cancel or delete

bash
curl -X DELETE "https://api.smartapihub.com/v1/video/generations/0c1f4c2a-…" \
  -H "Authorization: Bearer $LLM_API_KEY"
  • Job not terminal (queued, submitted, running) → cancellation is requested at the provider, the job moves to canceled, the cost reservation is released. Returns the job with status: "canceled".
  • Job terminal (succeeded, failed, canceled, expired) → artifacts and the job record are deleted. Returns 204 No Content.
  • If cancellation is impossible at that moment → 409 job_not_cancellable.

Job state machine

queuedsubmittedrunningsucceededfailedcanceledexpiredTerminal states: succeeded · failed · canceled · expired. canceled/expired reachable from queued, submitted, running (arrows abbreviated).
StatusMeaning
queuedAccepted and reserved; waiting for a worker.
submittedSent to the provider.
runningProvider is generating; progress may update.
succeededArtifacts available; cost_micro settled. Terminal.
failedProvider or pipeline failure; error populated; reservation released. Terminal.
canceledCancelled via DELETE. Terminal.
expiredexpires_at (default 24 h after creation) passed before completion. Terminal.

Webhooks

When webhook_url is set, the gateway POSTs to it once the job reaches succeeded or failed:

http
POST /hooks/llm-jobs HTTP/1.1
Content-Type: application/json
X-LLM-Signature: sha256=3f2a9c…e41b
X-Request-Id: req_01J6ZK3M9PQR7S8T9V

{"event":"job.completed","job":{ …MediaJob as returned by GET… }}
FieldValues
eventjob.completed or job.failed
jobThe full job object, including artifacts[] with signed URLs

Verifying X-LLM-Signature

The signature is sha256= followed by the hex HMAC-SHA256 of the raw request body using your organization's webhook secret (available in the dashboard). Always verify before trusting the payload, and compare in constant time.

typescript
import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';

const app = express();
app.post('/hooks/llm-jobs', express.raw({ type: 'application/json' }), (req, res) => {
  const header = req.header('X-LLM-Signature') ?? '';
  const expected = 'sha256=' + createHmac('sha256', process.env.LLM_WEBHOOK_SECRET!).update(req.body).digest('hex');
  const ok = header.length === expected.length && timingSafeEqual(Buffer.from(header), Buffer.from(expected));
  if (!ok) return res.status(401).end();

  const { event, job } = JSON.parse(req.body.toString('utf8'));
  if (event === 'job.completed') {
    // download job.artifacts[0].url before it expires
  }
  res.status(204).end();
});

Webhook delivery notes:

  • Respond with any 2xx quickly; do heavy work asynchronously.
  • The webhook URL must be https and resolve to a public address; private, loopback and cloud-metadata ranges are rejected at submit time (400 ssrf_blocked).
  • Treat webhooks as a hint and keep polling as a fallback for critical flows.

Errors

HTTPcodeCause
400idempotency_key_requiredHeader missing.
400validation_failedBad aspect_ratio, duration_seconds out of range, non-https URLs.
400ssrf_blockedwebhook_url / image_url not allowed.
402insufficient_credits / budget_exceeded / spending_limit_exceededReservation exceeds balance or budget.
404not_foundUnknown job id (or another organization's job).
409idempotency_conflictKey reused with a different body.
409job_not_cancellableDELETE while the job cannot be cancelled.
501not_implementedNo real video provider is wired in this deployment.