Qulture API
DocsAPI referencePlaygroundPricingConnectors
Sign in
Qulture API

The Qulture Studio API: AI video and images from your own app.

Build

  • Docs
  • API reference
  • Playground
  • Changelog

Account

  • Pricing
  • Console
  • Connectors

Qulture

  • Qulture Studio
  • Support
  • Terms
  • Privacy

© 2026 Qulture

Get started

  • Introduction
  • Quickstart
  • Authentication

Guides

  • Making videos
  • Images
  • Jobs & polling
  • Webhooks

Reference

  • Pricing & billing
  • Rate limits
  • Errors
  • API reference

More

  • Changelog
  • FAQ
Console & API keys PlaygroundAI ChatGPT & Claude (MCP)

Guides

Webhooks

Get called when a video is ready.

Instead of polling, let Qulture call your server when a video (or image) is done.

Adding a webhook

Add webhook_url to the create request. It must be a public https:// URL. Works on POST /jobs, /images, /images/:id/animate and on regenerate / extend / upgrade.

Create with a webhook
curl -X POST https://qulture-webapi-staging-381114038302.us-central1.run.app/api/v1/studio/jobs \
  -H "Authorization: Bearer $QULTURE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "mode": "text_to_video",
  "prompt": "A neon-lit street market at night, slow camera push in",
  "tier": "draft",
  "duration": 5,
  "aspect": "9:16",
  "webhook_url": "https://your.app/hooks/qulture"
}'

Events

eventWhen
video.succeededThe video is ready (status ready).
video.failedThe video failed or was blocked (status failed or blocked).
image.succeededThe image is ready.
image.failedThe image failed or was blocked.
pingSent by “Send test event” next to a key in the Console.

The body is JSON. data is the same object GET /jobs/:id returns:

POST to your webhook_url
Qulture-Signature: t=1793232000,v1=5f2b9c…
Qulture-Event: video.succeeded
Qulture-Delivery: 0d3c8a6e-…

{
  "id": "0d3c8a6e-…",
  "event": "video.succeeded",
  "created_at": "2026-10-29T00:21:12Z",
  "data": {
    "id": "8d67563e-…",
    "status": "ready",
    "video_url": "https://…/marked.mp4?…",
    "download_url": "https://…",
    "thumbnail_url": "https://…",
    "duration_s": 5,
    …
  }
}

Verifying signatures

Each API key has its own signing secret (whsec_…), shown when you create the key and any time in the Console. The Qulture-Signature header is t=<unix time>,v1=<signature>, where the signature is the hex HMAC-SHA256 of <t>.<raw body> with your secret. Check it, and reject timestamps older than 5 minutes:

Verify a webhook
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.QULTURE_WEBHOOK_SECRET; // whsec_…

// Keep the raw body: the signature is over the exact bytes.
app.post("/hooks/qulture", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("Qulture-Signature") ?? "";
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(`${parts.t}.${req.body}`)
    .digest("hex");

  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const ok = fresh && parts.v1 && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
  if (!ok) return res.status(400).send("bad signature");

  const event = JSON.parse(req.body);
  if (event.event === "video.succeeded") {
    console.log("Video ready:", event.data.video_url);
  }
  res.sendStatus(200);
});

app.listen(3000);

Retries

Reply with any 2xx within 15 seconds. Anything else (or a timeout) is retried after 10 s, 30 s, 2 min, 10 min, 30 min, 1 h, 3 h and 6 h. Use id (also Qulture-Delivery) to ignore duplicates. Redirects are not followed. If you revoke the key, its pending webhooks stop.

Testing locally

Use a tunnel such as ngrok http 3000 or cloudflared tunnel to get a public https URL for your laptop, then press Send test event next to your key in the Console.

← PreviousJobs & pollingNext →Pricing & billing

On this page

  • Adding a webhook
  • Events
  • Verifying signatures
  • Retries
  • Testing locally